diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index ca1022d..1dc11df 100644 --- a/Single-Header/dn_single_header.cpp +++ b/Single-Header/dn_single_header.cpp @@ -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)); @@ -15193,7 +15260,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, char *stderr_buffer, DN_USize *stderr_size, DN_U32 timeout_ms, - DN_ErrSink *err) + DN_ErrSink *err) { DN_OSExecResult result = {}; size_t stdout_buffer_size = 0; @@ -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); diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index c0117a9..d67cf39 100644 --- a/Single-Header/dn_single_header.h +++ b/Single-Header/dn_single_header.h @@ -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 { @@ -4396,7 +4403,8 @@ 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_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); diff --git a/Source/OS/dn_os_posix.cpp b/Source/OS/dn_os_posix.cpp index c904d34..81e9734 100644 --- a/Source/OS/dn_os_posix.cpp +++ b/Source/OS/dn_os_posix.cpp @@ -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)); diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index d275370..9cc7bb3 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -683,7 +683,7 @@ DN_API DN_OSExecResult DN_OS_ExecPump(DN_OSExecAsyncHandle handle, char *stderr_buffer, DN_USize *stderr_size, DN_U32 timeout_ms, - DN_ErrSink *err) + DN_ErrSink *err) { DN_OSExecResult result = {}; size_t stdout_buffer_size = 0; @@ -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); diff --git a/Source/OS/dn_os_windows.h b/Source/OS/dn_os_windows.h index 217f92e..40423b9 100644 --- a/Source/OS/dn_os_windows.h +++ b/Source/OS/dn_os_windows.h @@ -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); diff --git a/Source/dn.cpp b/Source/dn.cpp index 3a8f607..ee966f9 100644 --- a/Source/dn.cpp +++ b/Source/dn.cpp @@ -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 diff --git a/Source/dn.h b/Source/dn.h index b15e345..fc3d24b 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -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 { @@ -2466,7 +2473,8 @@ 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_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; };