Add detached threads impl, make init order more robust, print to console with UTF16
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// Generated by the DN single header generator 2026-07-15 17:58:16
|
||||
// Generated by the DN single header generator 2026-07-15 21:41:33
|
||||
|
||||
// DN: Single header generator commented out => #if defined(_CLANGD)
|
||||
// #define DN_WITH_TESTS 1
|
||||
@@ -2189,13 +2189,12 @@ DN_API void DN_TCInitFromHeap(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs arg
|
||||
|
||||
DN_API void DN_TCDeinit(DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas)
|
||||
{
|
||||
// NOTE: That we deallocate the main memory last as TC might be allocated in that arena.
|
||||
if (deinit_arenas == DN_TCDeinitArenas_Yes) {
|
||||
DN_MemListDeinit(tc->main_arena->mem);
|
||||
for (DN_ForIndexU(index, tc->temp_arenas_count)) {
|
||||
for (DN_ForIndexU(index, tc->temp_arenas_count))
|
||||
DN_MemListDeinit(tc->temp_arenas[index]->mem);
|
||||
DN_MemListDeinit(tc->temp_arenas[index]->mem);
|
||||
}
|
||||
DN_MemListDeinit(tc->err_sink.arena->mem);
|
||||
DN_MemListDeinit(tc->main_arena->mem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12369,11 +12368,32 @@ DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Str8Slice cmd_line, DN_OSExecArgs *a
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSThread
|
||||
static void DN_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context;
|
||||
|
||||
// NOTE: Wait on the semaphore, the thread that initiated the thread is going to retrieve the
|
||||
// thread ID, then set it the value on `thread->thread_id` pointer then increment this semaphore
|
||||
// to unblock this thread.
|
||||
//
|
||||
// This semaphore is deleted in the caller's thread when we unblock them by signalling the
|
||||
// semaphore below this.
|
||||
DN_OS_SemaphoreWait(&thread->thread_exec_wait_for_thread_id_sem, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
|
||||
// NOTE: Setup thread context (TLS) _after_ thread ID is setup.
|
||||
DN_TCInitFromHeap(&thread->context, thread->thread_id, thread->tc_init_args, DN_OS_HeapInitDefault());
|
||||
|
||||
// NOTE: Once all initialisation is done, if the thread is to be detached, make a copy of the
|
||||
// thread pointer because the caller is not guaranteeing to keep the thread pointer alive (they
|
||||
// can throw away the DN_OSThread since they want to detach the thread)
|
||||
//
|
||||
// Note that the caller _cannot_ throw away the thread pointer until we increment the semaphore
|
||||
// below as the OS implementation _should_ be sleeping on that semaphore.
|
||||
if (thread->flags & DN_OSThreadFlags_Detached)
|
||||
thread = DN_ArenaNewCopy(thread->context.main_arena, DN_OSThread, thread);
|
||||
|
||||
// NOTE: Equip the pointers into TLS only _after_ the thread context is copied (if it was
|
||||
// detached) to avoid potential dangling ref in the TLS.
|
||||
DN_TCEquip(&thread->context);
|
||||
if (thread->is_lane_set) {
|
||||
DN_OS_TCThreadLaneEquip(thread->lane);
|
||||
@@ -12381,8 +12401,55 @@ static void DN_OS_ThreadExecute_(void *user_context)
|
||||
} else {
|
||||
DN_OS_ThreadSetNameFmt("T%zu", thread->lane.index, thread->lane.count, thread->thread_id);
|
||||
}
|
||||
DN_OS_SemaphoreWait(&thread->init_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
|
||||
// NOTE: Now we can increment the semaphore that the caller's thread is waiting on now that we've
|
||||
// safely initialised the thread's contents and made a copy if necessary.
|
||||
DN_OS_SemaphoreIncrement(&thread->caller_wait_for_thread_init_to_finish_sem, 1);
|
||||
|
||||
// NOTE: Run the user's code
|
||||
thread->func(thread);
|
||||
|
||||
// NOTE: If we're detached, it's this thread's responsibility to cleanup itself. In the platform
|
||||
// layer it should have closed any references to the thread so we should just need to cleanup the
|
||||
// TLS.
|
||||
if (thread->flags & DN_OSThreadFlags_Detached)
|
||||
DN_TCDeinit(&thread->context, DN_TCDeinitArenas_Yes);
|
||||
}
|
||||
|
||||
static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context)
|
||||
{
|
||||
if (thread) {
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->thread_exec_wait_for_thread_id_sem = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->caller_wait_for_thread_init_to_finish_sem = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
thread->flags = init_args.flags;
|
||||
if (lane) {
|
||||
thread->is_lane_set = true;
|
||||
thread->lane = *lane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_OS_ThreadPostInit_(DN_OSThread *thread, bool result)
|
||||
{
|
||||
// NOTE: Ensure that thread_id is set before 'thread->func' is called.
|
||||
if (result) {
|
||||
// NOTE: Unblock the thread executor now that thread_id and flags have been set
|
||||
DN_OS_SemaphoreIncrement(&thread->thread_exec_wait_for_thread_id_sem, 1);
|
||||
|
||||
// NOTE: Wait for the thread to finish initialising before we leave
|
||||
DN_OS_SemaphoreWait(&thread->caller_wait_for_thread_init_to_finish_sem,
|
||||
DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
// NOTE: Clean up the semaphores
|
||||
DN_OS_SemaphoreDeinit(&thread->thread_exec_wait_for_thread_id_sem);
|
||||
DN_OS_SemaphoreDeinit(&thread->caller_wait_for_thread_init_to_finish_sem);
|
||||
|
||||
if (!result)
|
||||
*thread = {};
|
||||
}
|
||||
|
||||
DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault()
|
||||
@@ -12706,10 +12773,14 @@ DN_API void DN_OS_Print(DN_OSPrintDest dest, DN_Str8 string)
|
||||
DN_Assert(string.count < DN_Cast(unsigned long) - 1);
|
||||
unsigned long bytes_written = 0;
|
||||
(void)bytes_written;
|
||||
if (print_to_console)
|
||||
WriteConsoleA(print_handle, string.data, DN_Cast(unsigned long) string.count, &bytes_written, nullptr);
|
||||
else
|
||||
if (print_to_console) {
|
||||
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
|
||||
DN_Str16 string16 = DN_OS_W32Str8ToStr16(&scratch.arena, string);
|
||||
WriteConsoleW(print_handle, string16.data, DN_Cast(unsigned long) string16.count, &bytes_written, nullptr);
|
||||
DN_TCScratchEnd(&scratch);
|
||||
} else {
|
||||
WriteFile(print_handle, string.data, DN_Cast(unsigned long) string.count, &bytes_written, nullptr);
|
||||
}
|
||||
#else
|
||||
fprintf(dest == DN_OSPrintDest_Out ? stdout : stderr, "%.*s", DN_Str8PrintFmt(string));
|
||||
#endif
|
||||
@@ -14351,11 +14422,7 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (!thread)
|
||||
return result;
|
||||
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->lane = *lane;
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
DN_OS_ThreadPreInit_(thread, func, lane, init_args, user_context);
|
||||
|
||||
// TODO(doyle): Check if semaphore is valid
|
||||
// NOTE: pthread_t is essentially the thread ID. In Windows, the handle and
|
||||
@@ -14380,15 +14447,13 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (result) {
|
||||
DN_Memcpy(&thread->handle, &p_thread, sizeof(p_thread));
|
||||
DN_Memcpy(&thread->thread_id, &p_thread, sizeof(p_thread));
|
||||
if (thread->flags & DN_OSThreadFlags_Detached) {
|
||||
pthread_detach(p_thread);
|
||||
thread->handle = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
||||
} else {
|
||||
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
||||
*thread = {};
|
||||
}
|
||||
|
||||
DN_OS_ThreadPostInit_(thread, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -14396,6 +14461,8 @@ DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arena
|
||||
{
|
||||
bool result = false;
|
||||
if (thread && thread->handle) {
|
||||
DN_AssertF(DN_BitIsNotSet(thread->flags, DN_OSThreadFlags_Detached),
|
||||
"Detached threads should have their handle immediately closed and invalidated so this branch should never hit.");
|
||||
pthread_t thread_id = {};
|
||||
DN_Memcpy(&thread_id, &thread->thread_id, sizeof(thread_id));
|
||||
|
||||
@@ -15750,14 +15817,7 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (!thread)
|
||||
return result;
|
||||
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
if (lane) {
|
||||
thread->is_lane_set = true;
|
||||
thread->lane = *lane;
|
||||
}
|
||||
DN_OS_ThreadPreInit_(thread, func, lane, init_args, user_context);
|
||||
|
||||
// TODO(doyle): Check if semaphore is valid
|
||||
DWORD thread_id = 0;
|
||||
@@ -15769,25 +15829,25 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
0 /*creation_flags*/,
|
||||
&thread_id);
|
||||
|
||||
result = thread->handle != INVALID_HANDLE_VALUE;
|
||||
if (result)
|
||||
thread->thread_id = thread_id;
|
||||
|
||||
// NOTE: Ensure that thread_id is set before 'thread->func' is called.
|
||||
result = thread->handle != NULL;
|
||||
if (result) {
|
||||
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
||||
} else {
|
||||
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
||||
*thread = {};
|
||||
thread->thread_id = thread_id;
|
||||
if (thread->flags & DN_OSThreadFlags_Detached) {
|
||||
CloseHandle(thread->handle);
|
||||
thread->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DN_OS_ThreadPostInit_(thread, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas)
|
||||
{
|
||||
bool result = false;
|
||||
if (thread && thread->handle) {
|
||||
if (thread && thread->handle && thread->handle != INVALID_HANDLE_VALUE) {
|
||||
DN_AssertF(DN_BitIsNotSet(thread->flags, DN_OSThreadFlags_Detached),
|
||||
"Detached threads should have their handle immediately closed and invalidated so this branch should never hit.");
|
||||
DWORD wait_result = WaitForSingleObject(thread->handle, INFINITE);
|
||||
result = wait_result == WAIT_OBJECT_0;
|
||||
CloseHandle(thread->handle);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated by the DN single header generator 2026-07-15 17:58:16
|
||||
// Generated by the DN single header generator 2026-07-15 21:41:33
|
||||
|
||||
#if !defined(DN_H)
|
||||
#define DN_H
|
||||
@@ -4387,6 +4387,13 @@ struct DN_OSConditionVariable
|
||||
|
||||
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
|
||||
|
||||
typedef DN_U32 DN_OSThreadFlags;
|
||||
enum DN_OSThreadFlags_
|
||||
{
|
||||
DN_OSThreadFlags_Nil = 0,
|
||||
DN_OSThreadFlags_Detached = 1 << 0,
|
||||
};
|
||||
|
||||
typedef struct DN_OSThreadInitArgs DN_OSThreadInitArgs;
|
||||
struct DN_OSThreadInitArgs
|
||||
{
|
||||
@@ -4397,6 +4404,7 @@ struct DN_OSThreadInitArgs
|
||||
// NOTE: Set how much stack space in bytes the thread will have. Leave this value to 0 to defer
|
||||
// to the OS's default stack size.
|
||||
DN_USize stack_size;
|
||||
DN_OSThreadFlags flags;
|
||||
};
|
||||
|
||||
typedef struct DN_OSThreadLane DN_OSThreadLane;
|
||||
@@ -4420,6 +4428,7 @@ struct DN_OSThreadLaneway
|
||||
typedef struct DN_OSThread DN_OSThread;
|
||||
struct DN_OSThread
|
||||
{
|
||||
DN_OSThreadFlags flags;
|
||||
DN_Str8x64 name;
|
||||
DN_TCCore context;
|
||||
DN_OSThreadLane lane;
|
||||
@@ -4428,7 +4437,8 @@ struct DN_OSThread
|
||||
DN_U64 thread_id;
|
||||
void *user_context;
|
||||
DN_OSThreadFunc *func;
|
||||
DN_OSSemaphore init_semaphore;
|
||||
DN_OSSemaphore thread_exec_wait_for_thread_id_sem;
|
||||
DN_OSSemaphore caller_wait_for_thread_init_to_finish_sem;
|
||||
DN_TCInitArgs tc_init_args;
|
||||
};
|
||||
|
||||
@@ -7681,7 +7691,7 @@ DN_NETResponse DN_NET_EmcWaitForAnyResponse(DN_NETCore *net, DN_Arena *aren
|
||||
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
||||
__declspec(dllimport) BOOL __stdcall AllocConsole(VOID);
|
||||
__declspec(dllimport) BOOL __stdcall FreeConsole(VOID);
|
||||
__declspec(dllimport) BOOL __stdcall AttachConsole(DWORD dwProcessId);
|
||||
|
||||
@@ -1287,11 +1287,7 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (!thread)
|
||||
return result;
|
||||
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->lane = *lane;
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
DN_OS_ThreadPreInit_(thread, func, lane, init_args, user_context);
|
||||
|
||||
// TODO(doyle): Check if semaphore is valid
|
||||
// NOTE: pthread_t is essentially the thread ID. In Windows, the handle and
|
||||
@@ -1316,15 +1312,13 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (result) {
|
||||
DN_Memcpy(&thread->handle, &p_thread, sizeof(p_thread));
|
||||
DN_Memcpy(&thread->thread_id, &p_thread, sizeof(p_thread));
|
||||
if (thread->flags & DN_OSThreadFlags_Detached) {
|
||||
pthread_detach(p_thread);
|
||||
thread->handle = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
||||
} else {
|
||||
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
||||
*thread = {};
|
||||
}
|
||||
|
||||
DN_OS_ThreadPostInit_(thread, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1332,6 +1326,8 @@ DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arena
|
||||
{
|
||||
bool result = false;
|
||||
if (thread && thread->handle) {
|
||||
DN_AssertF(DN_BitIsNotSet(thread->flags, DN_OSThreadFlags_Detached),
|
||||
"Detached threads should have their handle immediately closed and invalidated so this branch should never hit.");
|
||||
pthread_t thread_id = {};
|
||||
DN_Memcpy(&thread_id, &thread->thread_id, sizeof(thread_id));
|
||||
|
||||
|
||||
+11
-18
@@ -1240,14 +1240,7 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
if (!thread)
|
||||
return result;
|
||||
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->init_semaphore = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
if (lane) {
|
||||
thread->is_lane_set = true;
|
||||
thread->lane = *lane;
|
||||
}
|
||||
DN_OS_ThreadPreInit_(thread, func, lane, init_args, user_context);
|
||||
|
||||
// TODO(doyle): Check if semaphore is valid
|
||||
DWORD thread_id = 0;
|
||||
@@ -1259,25 +1252,25 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_
|
||||
0 /*creation_flags*/,
|
||||
&thread_id);
|
||||
|
||||
result = thread->handle != INVALID_HANDLE_VALUE;
|
||||
if (result)
|
||||
thread->thread_id = thread_id;
|
||||
|
||||
// NOTE: Ensure that thread_id is set before 'thread->func' is called.
|
||||
result = thread->handle != NULL;
|
||||
if (result) {
|
||||
DN_OS_SemaphoreIncrement(&thread->init_semaphore, 1);
|
||||
} else {
|
||||
DN_OS_SemaphoreDeinit(&thread->init_semaphore);
|
||||
*thread = {};
|
||||
thread->thread_id = thread_id;
|
||||
if (thread->flags & DN_OSThreadFlags_Detached) {
|
||||
CloseHandle(thread->handle);
|
||||
thread->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
DN_OS_ThreadPostInit_(thread, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas)
|
||||
{
|
||||
bool result = false;
|
||||
if (thread && thread->handle) {
|
||||
if (thread && thread->handle && thread->handle != INVALID_HANDLE_VALUE) {
|
||||
DN_AssertF(DN_BitIsNotSet(thread->flags, DN_OSThreadFlags_Detached),
|
||||
"Detached threads should have their handle immediately closed and invalidated so this branch should never hit.");
|
||||
DWORD wait_result = WaitForSingleObject(thread->handle, INFINITE);
|
||||
result = wait_result == WAIT_OBJECT_0;
|
||||
CloseHandle(thread->handle);
|
||||
|
||||
@@ -383,7 +383,7 @@
|
||||
// NOTE: consoleapi.h ///////////////////////////////////////////////////////////////////////////
|
||||
extern "C"
|
||||
{
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleA(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
||||
__declspec(dllimport) BOOL __stdcall WriteConsoleW(HANDLE hConsoleOutput, const VOID* lpBuffer, DWORD nNumberOfCharsToWrite, DWORD *lpNumberOfCharsWritten, VOID *lpReserved);
|
||||
__declspec(dllimport) BOOL __stdcall AllocConsole(VOID);
|
||||
__declspec(dllimport) BOOL __stdcall FreeConsole(VOID);
|
||||
__declspec(dllimport) BOOL __stdcall AttachConsole(DWORD dwProcessId);
|
||||
|
||||
+80
-9
@@ -2187,13 +2187,12 @@ DN_API void DN_TCInitFromHeap(DN_TCCore *tc, DN_U64 thread_id, DN_TCInitArgs arg
|
||||
|
||||
DN_API void DN_TCDeinit(DN_TCCore *tc, DN_TCDeinitArenas deinit_arenas)
|
||||
{
|
||||
// NOTE: That we deallocate the main memory last as TC might be allocated in that arena.
|
||||
if (deinit_arenas == DN_TCDeinitArenas_Yes) {
|
||||
DN_MemListDeinit(tc->main_arena->mem);
|
||||
for (DN_ForIndexU(index, tc->temp_arenas_count)) {
|
||||
for (DN_ForIndexU(index, tc->temp_arenas_count))
|
||||
DN_MemListDeinit(tc->temp_arenas[index]->mem);
|
||||
DN_MemListDeinit(tc->temp_arenas[index]->mem);
|
||||
}
|
||||
DN_MemListDeinit(tc->err_sink.arena->mem);
|
||||
DN_MemListDeinit(tc->main_arena->mem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12367,11 +12366,32 @@ DN_API DN_OSExecResult DN_OS_ExecOrAbort(DN_Str8Slice cmd_line, DN_OSExecArgs *a
|
||||
return result;
|
||||
}
|
||||
|
||||
// NOTE: DN_OSThread
|
||||
static void DN_OS_ThreadExecute_(void *user_context)
|
||||
{
|
||||
DN_OSThread *thread = DN_Cast(DN_OSThread *) user_context;
|
||||
|
||||
// NOTE: Wait on the semaphore, the thread that initiated the thread is going to retrieve the
|
||||
// thread ID, then set it the value on `thread->thread_id` pointer then increment this semaphore
|
||||
// to unblock this thread.
|
||||
//
|
||||
// This semaphore is deleted in the caller's thread when we unblock them by signalling the
|
||||
// semaphore below this.
|
||||
DN_OS_SemaphoreWait(&thread->thread_exec_wait_for_thread_id_sem, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
|
||||
// NOTE: Setup thread context (TLS) _after_ thread ID is setup.
|
||||
DN_TCInitFromHeap(&thread->context, thread->thread_id, thread->tc_init_args, DN_OS_HeapInitDefault());
|
||||
|
||||
// NOTE: Once all initialisation is done, if the thread is to be detached, make a copy of the
|
||||
// thread pointer because the caller is not guaranteeing to keep the thread pointer alive (they
|
||||
// can throw away the DN_OSThread since they want to detach the thread)
|
||||
//
|
||||
// Note that the caller _cannot_ throw away the thread pointer until we increment the semaphore
|
||||
// below as the OS implementation _should_ be sleeping on that semaphore.
|
||||
if (thread->flags & DN_OSThreadFlags_Detached)
|
||||
thread = DN_ArenaNewCopy(thread->context.main_arena, DN_OSThread, thread);
|
||||
|
||||
// NOTE: Equip the pointers into TLS only _after_ the thread context is copied (if it was
|
||||
// detached) to avoid potential dangling ref in the TLS.
|
||||
DN_TCEquip(&thread->context);
|
||||
if (thread->is_lane_set) {
|
||||
DN_OS_TCThreadLaneEquip(thread->lane);
|
||||
@@ -12379,8 +12399,55 @@ static void DN_OS_ThreadExecute_(void *user_context)
|
||||
} else {
|
||||
DN_OS_ThreadSetNameFmt("T%zu", thread->lane.index, thread->lane.count, thread->thread_id);
|
||||
}
|
||||
DN_OS_SemaphoreWait(&thread->init_semaphore, DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
|
||||
// NOTE: Now we can increment the semaphore that the caller's thread is waiting on now that we've
|
||||
// safely initialised the thread's contents and made a copy if necessary.
|
||||
DN_OS_SemaphoreIncrement(&thread->caller_wait_for_thread_init_to_finish_sem, 1);
|
||||
|
||||
// NOTE: Run the user's code
|
||||
thread->func(thread);
|
||||
|
||||
// NOTE: If we're detached, it's this thread's responsibility to cleanup itself. In the platform
|
||||
// layer it should have closed any references to the thread so we should just need to cleanup the
|
||||
// TLS.
|
||||
if (thread->flags & DN_OSThreadFlags_Detached)
|
||||
DN_TCDeinit(&thread->context, DN_TCDeinitArenas_Yes);
|
||||
}
|
||||
|
||||
static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context)
|
||||
{
|
||||
if (thread) {
|
||||
thread->func = func;
|
||||
thread->user_context = user_context;
|
||||
thread->thread_exec_wait_for_thread_id_sem = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->caller_wait_for_thread_init_to_finish_sem = DN_OS_SemaphoreInit(0 /*initial_count*/);
|
||||
thread->tc_init_args = init_args.tc_args;
|
||||
thread->flags = init_args.flags;
|
||||
if (lane) {
|
||||
thread->is_lane_set = true;
|
||||
thread->lane = *lane;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DN_OS_ThreadPostInit_(DN_OSThread *thread, bool result)
|
||||
{
|
||||
// NOTE: Ensure that thread_id is set before 'thread->func' is called.
|
||||
if (result) {
|
||||
// NOTE: Unblock the thread executor now that thread_id and flags have been set
|
||||
DN_OS_SemaphoreIncrement(&thread->thread_exec_wait_for_thread_id_sem, 1);
|
||||
|
||||
// NOTE: Wait for the thread to finish initialising before we leave
|
||||
DN_OS_SemaphoreWait(&thread->caller_wait_for_thread_init_to_finish_sem,
|
||||
DN_OS_SEMAPHORE_INFINITE_TIMEOUT);
|
||||
}
|
||||
|
||||
// NOTE: Clean up the semaphores
|
||||
DN_OS_SemaphoreDeinit(&thread->thread_exec_wait_for_thread_id_sem);
|
||||
DN_OS_SemaphoreDeinit(&thread->caller_wait_for_thread_init_to_finish_sem);
|
||||
|
||||
if (!result)
|
||||
*thread = {};
|
||||
}
|
||||
|
||||
DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault()
|
||||
@@ -12704,10 +12771,14 @@ DN_API void DN_OS_Print(DN_OSPrintDest dest, DN_Str8 string)
|
||||
DN_Assert(string.count < DN_Cast(unsigned long) - 1);
|
||||
unsigned long bytes_written = 0;
|
||||
(void)bytes_written;
|
||||
if (print_to_console)
|
||||
WriteConsoleA(print_handle, string.data, DN_Cast(unsigned long) string.count, &bytes_written, nullptr);
|
||||
else
|
||||
if (print_to_console) {
|
||||
DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0);
|
||||
DN_Str16 string16 = DN_OS_W32Str8ToStr16(&scratch.arena, string);
|
||||
WriteConsoleW(print_handle, string16.data, DN_Cast(unsigned long) string16.count, &bytes_written, nullptr);
|
||||
DN_TCScratchEnd(&scratch);
|
||||
} else {
|
||||
WriteFile(print_handle, string.data, DN_Cast(unsigned long) string.count, &bytes_written, nullptr);
|
||||
}
|
||||
#else
|
||||
fprintf(dest == DN_OSPrintDest_Out ? stdout : stderr, "%.*s", DN_Str8PrintFmt(string));
|
||||
#endif
|
||||
|
||||
+11
-1
@@ -2457,6 +2457,13 @@ struct DN_OSConditionVariable
|
||||
|
||||
typedef DN_I32(DN_OSThreadFunc)(struct DN_OSThread *);
|
||||
|
||||
typedef DN_U32 DN_OSThreadFlags;
|
||||
enum DN_OSThreadFlags_
|
||||
{
|
||||
DN_OSThreadFlags_Nil = 0,
|
||||
DN_OSThreadFlags_Detached = 1 << 0,
|
||||
};
|
||||
|
||||
typedef struct DN_OSThreadInitArgs DN_OSThreadInitArgs;
|
||||
struct DN_OSThreadInitArgs
|
||||
{
|
||||
@@ -2467,6 +2474,7 @@ struct DN_OSThreadInitArgs
|
||||
// NOTE: Set how much stack space in bytes the thread will have. Leave this value to 0 to defer
|
||||
// to the OS's default stack size.
|
||||
DN_USize stack_size;
|
||||
DN_OSThreadFlags flags;
|
||||
};
|
||||
|
||||
typedef struct DN_OSThreadLane DN_OSThreadLane;
|
||||
@@ -2490,6 +2498,7 @@ struct DN_OSThreadLaneway
|
||||
typedef struct DN_OSThread DN_OSThread;
|
||||
struct DN_OSThread
|
||||
{
|
||||
DN_OSThreadFlags flags;
|
||||
DN_Str8x64 name;
|
||||
DN_TCCore context;
|
||||
DN_OSThreadLane lane;
|
||||
@@ -2498,7 +2507,8 @@ struct DN_OSThread
|
||||
DN_U64 thread_id;
|
||||
void *user_context;
|
||||
DN_OSThreadFunc *func;
|
||||
DN_OSSemaphore init_semaphore;
|
||||
DN_OSSemaphore thread_exec_wait_for_thread_id_sem;
|
||||
DN_OSSemaphore caller_wait_for_thread_init_to_finish_sem;
|
||||
DN_TCInitArgs tc_init_args;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user