diff --git a/Single-Header/dn_single_header.cpp b/Single-Header/dn_single_header.cpp index c721a7f..05cdb10 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-16 23:31:53 +// Generated by the DN single header generator 2026-07-17 17:54:43 // DN: Single header generator commented out => #if defined(_CLANGD) // #define DN_WITH_TESTS 1 @@ -12445,8 +12445,18 @@ static void DN_OS_ThreadExecute_(void *user_context) // 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) + if (thread->flags & DN_OSThreadFlags_Detached) { + #if !defined(DN_PLATFORM_WIN32) + // NOTE: Cleanup the semaphore since we're detached, all left-over resources need to be cleaned + // up ourselves. + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + #endif DN_TCDeinit(&thread->context, DN_TCDeinitArenas_Yes); + } else { + #if !defined(DN_PLATFORM_WIN32) + DN_OS_SemaphoreIncrement(&thread->join_done_sem, 1); // NOTE: Signal for DN_OS_ThreadJoin waits on this. + #endif + } } static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context) @@ -12456,6 +12466,9 @@ static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ 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*/); +#if !defined(DN_PLATFORM_WIN32) + thread->join_done_sem = DN_OS_SemaphoreInit(0 /*initial_count*/); +#endif thread->tc_init_args = init_args.tc_args; thread->flags = init_args.flags; if (lane) { @@ -12481,8 +12494,12 @@ static void DN_OS_ThreadPostInit_(DN_OSThread *thread, bool result) 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) + if (!result) { + #if !defined(DN_PLATFORM_WIN32) + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + #endif *thread = {}; + } } DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault() @@ -12600,10 +12617,10 @@ DN_API void DN_OS_ThreadLanewayDispatch(DN_OSThreadLaneway *laneway, DN_OSThread } } -DN_API void DN_OS_ThreadLanewayJoin(DN_OSThreadLaneway *laneway, DN_TCDeinitArenas deinit_arenas) +DN_API void DN_OS_ThreadLanewayJoin(DN_OSThreadLaneway *laneway, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { for (DN_ForItSize(it, DN_OSThread, laneway->threads, laneway->threads_count)) - DN_OS_ThreadJoin(it.data, deinit_arenas); + DN_OS_ThreadJoin(it.data, timeout_ms, deinit_arenas); DN_OS_BarrierDeinit(&laneway->barrier); } @@ -12687,7 +12704,7 @@ DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async) DN_AtomicSetValue32(&async->join_threads, true); DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) - DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); + DN_OS_ThreadJoin(it.data, UINT32_MAX, DN_TCDeinitArenas_Yes); } static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, DN_U64 wait_time_ms) { @@ -12729,7 +12746,7 @@ DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWork result.completion_sem = DN_OS_SemaphoreInit(0); result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms); if (!result.queued) - DN_OS_SemaphoreDeinit(&result.completion_sem); + DN_OS_SemaphoreDeinit(&result.completion_sem); return result; } @@ -14490,20 +14507,26 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ return result; } -DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas) +DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { - bool result = false; + bool result = true; 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)); - void *return_val = nullptr; - result = pthread_join(thread_id, &return_val) == 0; - thread->handle = {}; - thread->thread_id = {}; - DN_TCDeinit(&thread->context, deinit_arenas); + DN_OSSemaphoreWaitResult wait_result = DN_OS_SemaphoreWait(&thread->join_done_sem, timeout_ms); + if (wait_result == DN_OSSemaphoreWaitResult_Success) { + void *return_val = {}; + pthread_join(thread_id, &return_val); + thread->handle = {}; + thread->thread_id = {}; + DN_TCDeinit(&thread->context, deinit_arenas); + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + } else { + result = false; + } } return result; } @@ -15917,18 +15940,21 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ return result; } -DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas) +DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { - bool result = false; + bool result = true; 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); - thread->handle = INVALID_HANDLE_VALUE; - thread->thread_id = {}; - DN_TCDeinit(&thread->context, deinit_arenas); + DWORD wait_result = WaitForSingleObject(thread->handle, timeout_ms); + if (wait_result == WAIT_OBJECT_0) { + CloseHandle(thread->handle); + thread->handle = INVALID_HANDLE_VALUE; + thread->thread_id = {}; + DN_TCDeinit(&thread->context, deinit_arenas); + } else { + result = false; + } } return result; } @@ -16939,7 +16965,7 @@ void DN_NET_CurlDeinit(DN_NETCore *net) DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context; curl->kill_thread = true; curl_multi_wakeup(curl->thread_curlm); - DN_OS_ThreadJoin(&curl->thread, DN_TCDeinitArenas_Yes); + DN_OS_ThreadJoin(&curl->thread, UINT32_MAX, DN_TCDeinitArenas_Yes); } static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) diff --git a/Single-Header/dn_single_header.h b/Single-Header/dn_single_header.h index d241d2c..67ea1f7 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-16 23:31:53 +// Generated by the DN single header generator 2026-07-17 17:54:42 #if !defined(DN_H) #define DN_H @@ -4442,6 +4442,9 @@ struct DN_OSThread DN_OSThreadFunc *func; DN_OSSemaphore thread_exec_wait_for_thread_id_sem; DN_OSSemaphore caller_wait_for_thread_init_to_finish_sem; +#if !defined(DN_PLATFORM_WIN32) + DN_OSSemaphore join_done_sem; +#endif DN_TCInitArgs tc_init_args; }; @@ -6701,9 +6704,13 @@ DN_API void DN_OS_ConditionVariableBroadcast (D // // For the general use-case if you wish to use lanes then prefer the `DN_OS_ThreadLane` APIs // below which create the threads with the lane information setup accordingly. + +// DN_OS_ThreadJoin +// Returns true if the thread joined successfully or it is already joined. False if timed out +// waiting for the thread to join. DN_API bool DN_OS_ThreadInitLane (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context); DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadInitArgs init_args, void *user_context); -DN_API bool DN_OS_ThreadJoin (DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas); +DN_API bool DN_OS_ThreadJoin (DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas); DN_API DN_U32 DN_OS_ThreadID (); DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault (); DN_API void DN_OS_ThreadSetNameFmt (char const *fmt, ...); @@ -6777,7 +6784,7 @@ DN_API DN_V2USize DN_OS_ThreadLaneRange (D DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArgs (DN_OSThread* threads, DN_USize threads_count, DN_UPtr* shared_mem); DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArena (DN_USize threads_count, DN_Arena* arena); DN_API void DN_OS_ThreadLanewayDispatch (DN_OSThreadLaneway *laneway, DN_OSThreadFunc *entry_point, DN_OSThreadInitArgs init_args, void *user_context); -DN_API void DN_OS_ThreadLanewayJoin (DN_OSThreadLaneway *laneway, DN_TCDeinitArenas deinit_arenas); +DN_API void DN_OS_ThreadLanewayJoin (DN_OSThreadLaneway *laneway, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas); DN_API DN_OSThreadLane* DN_OS_TCThreadLane (); DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share); diff --git a/Source/Extra/dn_seshc.cpp b/Source/Extra/dn_seshc.cpp index c6c1f77..e880fb7 100644 --- a/Source/Extra/dn_seshc.cpp +++ b/Source/Extra/dn_seshc.cpp @@ -1,11 +1,13 @@ #include "dn_seshc.h" -struct DN_SHSendDMThreadContext_ +struct DN_SHMsgSendAsyncContext_ { - DN_Arena arena; - DN_SHCLIArgs args; - DN_Str8 sesh_pkey_hex; - DN_Str8 msg; + bool send_success; + DN_Arena* arena; + DN_SHCLIArgs args; + DN_SHMsgSendData send; + DN_Str8 msg; + bool detached; }; DN_SHCLIArgs DN_SH_CLIArgsCopy(DN_SHCLIArgs const *src, DN_Arena *arena) @@ -13,10 +15,8 @@ DN_SHCLIArgs DN_SH_CLIArgsCopy(DN_SHCLIArgs const *src, DN_Arena *arena) DN_SHCLIArgs result = {}; result = *src; result.exe_path = DN_Str8FromStr8Arena(src->exe_path, arena); - result.account_secret = DN_Str8FromStr8Arena(src->account_secret, arena); + result.ini_path = DN_Str8FromStr8Arena(src->ini_path, arena); result.seed_node_url = DN_Str8FromStr8Arena(src->seed_node_url, arena); - result.display_name = DN_Str8FromStr8Arena(src->display_name, arena); - result.recipient_pkey = DN_Str8FromStr8Arena(src->recipient_pkey, arena); return result; } @@ -25,16 +25,10 @@ DN_Str8 DN_SH_CLIOptionsStr8() DN_Str8 result = DN_Str8Lit( "SESHC OPTIONS (note all options must be set to activate Seshc):\n" " --seshc-exe-path Path to the Seshc EXE file to invoke for sending messages\n" -" --seshc-ini-path Path to the .ini file to load the secret/recipient public\n" -" key (only used if you are using `ini:` syntax)\n" -" --seshc-account-secret Specify the secret of the Session account to send a message\n" -" as (see --help on the Seshc EXE for more info)\n" +" --seshc-ini-path Path to the .ini file to load Seshc arguments from\n" +" (such as account secrets, display name, recipient...)\n" " --seshc-seed-node URL to the Session seed node for determining the nodes to\n" " send the message to\n" -" --seshc-display-name Display name to send the message as\n" -" --seshc-recipient-pkey Recipient to send the message as (must be 0x05 prefixed, 0x\n" -" is optional), or alternatively `ini:` syntax (see --help on\n" -" the Seshc EXE for more info)." ); return result; } @@ -75,46 +69,19 @@ DN_SHCLIEatArgResult DN_SH_CLIEatArg(DN_SHCLIArgs *args, char const **arg_ptr, i } } - if (!result.eaten && DN_Str8EqInsensitive(arg, DN_Str8Lit("--seshc-account-secret"))) { - (void)DN_ArgShift(result.arg_ptr, result.arg_count); - result.eaten = true; - args->account_secret = DN_Str8FromCStr8(DN_ArgShift(result.arg_ptr, result.arg_count)); - } - if (!result.eaten && DN_Str8EqInsensitive(arg, DN_Str8Lit("--seshc-seed-node"))) { (void)DN_ArgShift(result.arg_ptr, result.arg_count); result.eaten = true; args->seed_node_url = DN_Str8FromCStr8(DN_ArgShift(result.arg_ptr, result.arg_count)); } - - if (!result.eaten && DN_Str8EqInsensitive(arg, DN_Str8Lit("--seshc-display-name"))) { - (void)DN_ArgShift(result.arg_ptr, result.arg_count); - result.eaten = true; - args->display_name = DN_Str8FromCStr8(DN_ArgShift(result.arg_ptr, result.arg_count)); - } - - if (!result.eaten && DN_Str8EqInsensitive(arg, DN_Str8Lit("--seshc-recipient-pkey"))) { - (void)DN_ArgShift(result.arg_ptr, result.arg_count); - result.eaten = true; - args->recipient_pkey = DN_Str8FromCStr8(DN_ArgShift(result.arg_ptr, result.arg_count)); - - DN_Str8 check = DN_Str8TrimHexPrefix(args->recipient_pkey); - bool ini_prefixed = DN_Str8StartsWithSensitive(check, DN_Str8Lit("ini:")); - bool sesh_prefixed = !ini_prefixed && (DN_Str8StartsWithSensitive(check, DN_Str8Lit("05")) || DN_Str8StartsWithInsensitive(check, DN_Str8Lit("0x05"))); - if (!ini_prefixed && !sesh_prefixed) { - result.error_msg = DN_Str8Lit("Seshc recipient public key does not specify a 05 prefixed public key, or, use `ini:` to specify a public key in the .ini file"); - result.error_arg = args->recipient_pkey; - args->recipient_pkey = {}; - } - } } - if (args->seed_node_url.count && args->account_secret.count && args->exe_path.count && args->recipient_pkey.count) + if (args->seed_node_url.count && args->exe_path.count && args->ini_path.count) args->valid = true; return result; } -bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, char const *fmt, ...) +bool DN_SH_MsgSendBlockingFmt(DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, char const *fmt, ...) { bool result = false; if (cli_args->valid) { @@ -136,23 +103,21 @@ bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, c DN_LArrayAppend(cmds, &cmds_count, cli_args->exe_path); DN_LArrayAppend(cmds, &cmds_count, account_secret_arg); - DN_LArrayAppend(cmds, &cmds_count, cli_args->account_secret); + DN_LArrayAppend(cmds, &cmds_count, send.account_secret); DN_LArrayAppend(cmds, &cmds_count, seed_node_url_arg); DN_LArrayAppend(cmds, &cmds_count, cli_args->seed_node_url); - if (cli_args->display_name.count) { + if (send.display_name.count) { DN_LArrayAppend(cmds, &cmds_count, display_name_arg); - DN_LArrayAppend(cmds, &cmds_count, cli_args->display_name); + DN_LArrayAppend(cmds, &cmds_count, send.display_name); } - if (cli_args->ini_path.count) { - DN_LArrayAppend(cmds, &cmds_count, ini_path_arg); - DN_LArrayAppend(cmds, &cmds_count, cli_args->ini_path); - } + DN_LArrayAppend(cmds, &cmds_count, ini_path_arg); + DN_LArrayAppend(cmds, &cmds_count, cli_args->ini_path); DN_LArrayAppend(cmds, &cmds_count, send_arg); - DN_LArrayAppend(cmds, &cmds_count, sesh_pkey); + DN_LArrayAppend(cmds, &cmds_count, send.recipient); DN_LArrayAppend(cmds, &cmds_count, msg); @@ -167,7 +132,7 @@ bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, c result = exec.os_error_code == 0 && exec.exit_code == 0; if (!result) { DN_LogErrorF("Error sending message to %.*s (exit code: %d, os: %d): %.*s", - DN_Str8PrintFmt(cli_args->recipient_pkey), + DN_Str8PrintFmt(send.recipient), exec.exit_code, exec.os_error_code, DN_Str8PrintFmt(exec.stderr_text)); @@ -178,59 +143,76 @@ bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, c return result; } -static int DN_SH_CLIArgsSendDMDetachedFmtThreadFunc_(DN_OSThread *thread) +static int DN_SH_MsgSendDetachedFmtThreadFunc_(DN_OSThread *thread) { - DN_SHSendDMThreadContext_ *context = DN_Cast(DN_SHSendDMThreadContext_ *) thread->user_context; - DN_SH_CLIArgsSendDMBlockingFmt(&context->args, context->sesh_pkey_hex, "%.*s", DN_Str8PrintFmt(context->msg)); - DN_ArenaDeinit(&context->arena); + DN_SHMsgSendAsyncContext_ *context = DN_Cast(DN_SHMsgSendAsyncContext_ *) thread->user_context; + context->send_success = DN_SH_MsgSendBlockingFmt(context->send, &context->args, "%.*s", DN_Str8PrintFmt(context->msg)); + if (context->detached) + DN_ArenaDeinit(context->arena); return 0; } -void DN_SH_CLIArgsSendDMDetachedFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, char const *fmt, ...) +static DN_SHMsgSendAsyncHandle DN_SH_MsgSendAsyncFmtVMaybeDetached_(DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Arena *arena, bool detached, char const *fmt, va_list args) { + DN_SHMsgSendAsyncHandle result = {}; if (!cli_args->valid) - return; + return result; // NOTE: Setup context - DN_Arena arena = DN_OS_ArenaFromHeapVirtual(0, 0, DN_MemFlags_Nil); - DN_SHSendDMThreadContext_ *context = DN_ArenaNewZ(&arena, DN_SHSendDMThreadContext_); - context->args = DN_SH_CLIArgsCopy(cli_args, &arena); - context->sesh_pkey_hex = DN_Str8FromStr8Arena(sesh_pkey, &arena); - - va_list args; - va_start(args, fmt); - context->msg = DN_Str8FromFmtVArena(&arena, fmt, args); - va_end(args); - - // NOTE: Set the arena last after all allocations - context->arena = arena; - - // NOTE: Setup thread to be detached - DN_OSThread thread = {}; - DN_OSThreadInitArgs thread_args = DN_OS_ThreadInitArgsDefault(); - thread_args.flags |= DN_OSThreadFlags_Detached; + DN_SHMsgSendAsyncContext_ *context = DN_ArenaNewZ(arena, DN_SHMsgSendAsyncContext_); + context->args = DN_SH_CLIArgsCopy(cli_args, arena); + context->send = send; + context->arena = arena; + context->msg = DN_Str8FromFmtVArena(arena, fmt, args); + context->detached = detached; // NOTE: Execute and forget - DN_OS_ThreadInit(&thread, DN_SH_CLIArgsSendDMDetachedFmtThreadFunc_, thread_args, context); + DN_OSThreadInitArgs thread_args = DN_OS_ThreadInitArgsDefault(); + if (detached) + thread_args.flags |= DN_OSThreadFlags_Detached; + + DN_OS_ThreadInit(&result.thread, DN_SH_MsgSendDetachedFmtThreadFunc_, thread_args, context); + return result; } -void DN_SH_LogBroadcastBlockingFmt(DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...) +void DN_SH_MsgSendDetachedFmt(DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, char const *fmt, ...) { + DN_Arena arena_stack = DN_OS_ArenaFromHeapVirtual(0, 0, DN_MemFlags_Nil); + DN_Arena *arena = DN_ArenaNewZ(&arena_stack, DN_Arena); + *arena = arena_stack; + va_list args; va_start(args, fmt); - DN_SH_LogBroadcastFmtV(DN_SHLogMode_Blocking, type, cli_args, short_desc, fmt, args); + DN_SH_MsgSendAsyncFmtVMaybeDetached_(send, cli_args, arena, /*detached=*/ true, fmt, args); va_end(args); } -void DN_SH_LogBroadcastDetachedFmt(DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...) +DN_SHMsgSendAsyncHandle DN_SH_MsgSendAsyncFmt(DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Arena *arena, char const *fmt, ...) { va_list args; va_start(args, fmt); - DN_SH_LogBroadcastFmtV(DN_SHLogMode_Detached, type, cli_args, short_desc, fmt, args); + DN_SHMsgSendAsyncHandle result = DN_SH_MsgSendAsyncFmtVMaybeDetached_(send, cli_args, arena, /*detached=*/ false, fmt, args); va_end(args); + return result; } -void DN_SH_LogBroadcastFmtV(DN_SHLogMode mode, DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, va_list args) +DN_SHMsgSendAsyncStatus DN_SH_MsgSendAsyncWait(DN_SHMsgSendAsyncHandle *handle, DN_U32 timeout_ms) +{ + DN_SHMsgSendAsyncStatus result = DN_SHMsgSendAsyncStatus_Success; + if (handle && handle->thread.user_context) { + bool joined = DN_OS_ThreadJoin(&handle->thread, timeout_ms, DN_TCDeinitArenas_Yes); + if (joined) { + DN_SHMsgSendAsyncContext_ *context = DN_Cast(DN_SHMsgSendAsyncContext_ *) handle->thread.user_context; + result = context->send_success ? DN_SHMsgSendAsyncStatus_Success : DN_SHMsgSendAsyncStatus_Failed; + *handle = {}; + } else { + result = DN_SHMsgSendAsyncStatus_Timeout; + } + } + return result; +} + +void DN_SH_LogBroadcastFmtV(DN_SHLogMode mode, DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, va_list args) { DN_TCScratch scratch = DN_TCScratchBeginArena(nullptr, 0); DN_Str8 msg = DN_Str8FromFmtVArena(&scratch.arena, fmt, args); @@ -266,40 +248,56 @@ void DN_SH_LogBroadcastFmtV(DN_SHLogMode mode, DN_LogType type, DN_SHCLIArgs *cl time_emoji = DN_Str8Lit("🌑"); if (mode == DN_SHLogMode_Detached) { - DN_SH_CLIArgsSendDMDetachedFmt(cli_args, - cli_args->recipient_pkey, - "%.*s%.*s\n" - "%.*s %u-%02u-%02u %u:%02u:%02u %s\n" - "%.*s", - DN_Str8PrintFmt(prefix), - DN_Str8PrintFmt(short_desc), - DN_Str8PrintFmt(time_emoji), - date.year, - date.month, - date.day, - hour12, - date.minutes, - date.seconds, - am_pm, - DN_Str8PrintFmt(msg)); + DN_SH_MsgSendDetachedFmt(send, + cli_args, + "%.*s%.*s\n" + "%.*s %u-%02u-%02u %u:%02u:%02u %s\n" + "%.*s", + DN_Str8PrintFmt(prefix), + DN_Str8PrintFmt(short_desc), + DN_Str8PrintFmt(time_emoji), + date.year, + date.month, + date.day, + hour12, + date.minutes, + date.seconds, + am_pm, + DN_Str8PrintFmt(msg)); } else { DN_Assert(mode == DN_SHLogMode_Blocking); - DN_SH_CLIArgsSendDMBlockingFmt(cli_args, - cli_args->recipient_pkey, - "%.*s %.*s\n" - "%.*s %u-%02u-%02u %u:%02u:%02u %s\n" - "%.*s", - DN_Str8PrintFmt(prefix), - DN_Str8PrintFmt(short_desc), - DN_Str8PrintFmt(time_emoji), - date.year, - date.month, - date.day, - hour12, - date.minutes, - date.seconds, - am_pm, - DN_Str8PrintFmt(msg)); + DN_SH_MsgSendBlockingFmt(send, + cli_args, + "%.*s %.*s\n" + "%.*s %u-%02u-%02u %u:%02u:%02u %s\n" + "%.*s", + DN_Str8PrintFmt(prefix), + DN_Str8PrintFmt(short_desc), + DN_Str8PrintFmt(time_emoji), + date.year, + date.month, + date.day, + hour12, + date.minutes, + date.seconds, + am_pm, + DN_Str8PrintFmt(msg)); } DN_TCScratchEnd(&scratch); } + +void DN_SH_LogBroadcastBlockingFmt(DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + DN_SH_LogBroadcastFmtV(DN_SHLogMode_Blocking, type, send, cli_args, short_desc, fmt, args); + va_end(args); +} + +void DN_SH_LogBroadcastDetachedFmt(DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...) +{ + va_list args; + va_start(args, fmt); + DN_SH_LogBroadcastFmtV(DN_SHLogMode_Detached, type, send, cli_args, short_desc, fmt, args); + va_end(args); +} diff --git a/Source/Extra/dn_seshc.h b/Source/Extra/dn_seshc.h index e27b5bd..7ad7a68 100644 --- a/Source/Extra/dn_seshc.h +++ b/Source/Extra/dn_seshc.h @@ -11,10 +11,7 @@ struct DN_SHCLIArgs bool valid; DN_Str8 exe_path; DN_Str8 ini_path; - DN_Str8 account_secret; DN_Str8 seed_node_url; - DN_Str8 display_name; - DN_Str8 recipient_pkey; }; struct DN_SHCLIEatArgResult @@ -26,23 +23,60 @@ struct DN_SHCLIEatArgResult DN_USize arg_count; // Next argument count that that the caller should assume }; +struct DN_SHMsgSendData +{ + DN_Str8 recipient; // Either `ini:` or "05-prefix session account ID" + DN_Str8 account_secret; // Seshc EXE --account-secret format + DN_Str8 display_name; +}; + +struct DN_SHMsgSendAsyncHandle +{ + DN_OSThread thread; +}; + enum DN_SHLogMode { DN_SHLogMode_Blocking, DN_SHLogMode_Detached, }; +enum DN_SHMsgSendAsyncStatus +{ + DN_SHMsgSendAsyncStatus_Timeout, + DN_SHMsgSendAsyncStatus_Success, + DN_SHMsgSendAsyncStatus_Failed, +}; + + // NOTE: Seshc // Overview // Helper library that provides some useful APIs for programatically calling the Seshc CLI // executable to send messages from your C/C++ application. -DN_SHCLIArgs DN_SH_CLIArgsCopy (DN_SHCLIArgs const *src, DN_Arena *arena); -DN_Str8 DN_SH_CLIOptionsStr8 (); -DN_SHCLIEatArgResult DN_SH_CLIEatArg (DN_SHCLIArgs *args, char const **arg_ptr, int arg_count); -bool DN_SH_CLIArgsSendDMBlockingFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, char const *fmt, ...); -void DN_SH_CLIArgsSendDMDetachedFmt(DN_SHCLIArgs *cli_args, DN_Str8 sesh_pkey, char const *fmt, ...); -void DN_SH_LogBroadcastFmtV (DN_SHLogMode mode, DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, va_list args); -void DN_SH_LogBroadcastBlockingFmt (DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...); -void DN_SH_LogBroadcastDetachedFmt (DN_LogType type, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...); + +// API +// DN_SH_MsgSendDetachedFmt +// Send a 1-on-1 message with a background thread that is spawned on demand and detached. If the +// application exits before this thread has completed sending the message, the message will not +// be sent. + +// DN_SH_MsgSendBlockingFmt +// Send a 1-on-1 message using the current executing thread, blocking control-flow until the +// message has been sent. Returns true if the send was successful, false otherwise. + +// DN_SH_MsgSendAsyncFmt +// Send a 1-on-1 message with a background thread that is spawned on demand. A handle to the +// thread is returned that can be waited on. + +DN_SHCLIArgs DN_SH_CLIArgsCopy (DN_SHCLIArgs const *src, DN_Arena *arena); +DN_Str8 DN_SH_CLIOptionsStr8 (); +DN_SHCLIEatArgResult DN_SH_CLIEatArg (DN_SHCLIArgs *args, char const **arg_ptr, int arg_count); +bool DN_SH_MsgSendBlockingFmt (DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, char const *fmt, ...); +void DN_SH_MsgSendDetachedFmt (DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, char const *fmt, ...); +DN_SHMsgSendAsyncHandle DN_SH_MsgSendAsyncFmt (DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Arena *arena, char const *fmt, ...); +DN_SHMsgSendAsyncStatus DN_SH_MsgSendAsyncWait (DN_SHMsgSendAsyncHandle *handle, DN_U32 timeout_ms); +void DN_SH_LogBroadcastFmtV (DN_SHLogMode mode, DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, va_list args); +void DN_SH_LogBroadcastBlockingFmt (DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...); +void DN_SH_LogBroadcastDetachedFmt (DN_LogType type, DN_SHMsgSendData send, DN_SHCLIArgs *cli_args, DN_Str8 short_desc, char const *fmt, ...); #endif // #if !defined(DN_SESHC_H) diff --git a/Source/OS/dn_os_posix.cpp b/Source/OS/dn_os_posix.cpp index 81e9734..a8290c9 100644 --- a/Source/OS/dn_os_posix.cpp +++ b/Source/OS/dn_os_posix.cpp @@ -1322,20 +1322,26 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ return result; } -DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas) +DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { - bool result = false; + bool result = true; 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)); - void *return_val = nullptr; - result = pthread_join(thread_id, &return_val) == 0; - thread->handle = {}; - thread->thread_id = {}; - DN_TCDeinit(&thread->context, deinit_arenas); + DN_OSSemaphoreWaitResult wait_result = DN_OS_SemaphoreWait(&thread->join_done_sem, timeout_ms); + if (wait_result == DN_OSSemaphoreWaitResult_Success) { + void *return_val = {}; + pthread_join(thread_id, &return_val); + thread->handle = {}; + thread->thread_id = {}; + DN_TCDeinit(&thread->context, deinit_arenas); + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + } else { + result = false; + } } return result; } diff --git a/Source/OS/dn_os_w32.cpp b/Source/OS/dn_os_w32.cpp index ab52f82..943d81e 100644 --- a/Source/OS/dn_os_w32.cpp +++ b/Source/OS/dn_os_w32.cpp @@ -1307,18 +1307,21 @@ DN_API bool DN_OS_ThreadInitLane(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ return result; } -DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas) +DN_API bool DN_OS_ThreadJoin(DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { - bool result = false; + bool result = true; 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); - thread->handle = INVALID_HANDLE_VALUE; - thread->thread_id = {}; - DN_TCDeinit(&thread->context, deinit_arenas); + DWORD wait_result = WaitForSingleObject(thread->handle, timeout_ms); + if (wait_result == WAIT_OBJECT_0) { + CloseHandle(thread->handle); + thread->handle = INVALID_HANDLE_VALUE; + thread->thread_id = {}; + DN_TCDeinit(&thread->context, deinit_arenas); + } else { + result = false; + } } return result; } diff --git a/Source/Standalone/dn_ini.c b/Source/Standalone/dn_ini.c index b38343f..8ad5850 100644 --- a/Source/Standalone/dn_ini.c +++ b/Source/Standalone/dn_ini.c @@ -220,7 +220,6 @@ DN_INIToken DN_INI_NextToken(DN_INITokeniser const *tokeniser) tokeniser->prev_token.type == DN_INITokenType_MultilineValue || tokeniser->prev_token.type == DN_INITokenType_Section) { result.type = DN_INITokenType_EndOfStream; - break; } else { result.type = DN_INITokenType_Error; result.error = DN_INIStr8Lit("Premature end of stream encountered malforming the last key-value pair"); diff --git a/Source/dn.cpp b/Source/dn.cpp index e5c9e8e..16d427d 100644 --- a/Source/dn.cpp +++ b/Source/dn.cpp @@ -12443,8 +12443,18 @@ static void DN_OS_ThreadExecute_(void *user_context) // 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) + if (thread->flags & DN_OSThreadFlags_Detached) { + #if !defined(DN_PLATFORM_WIN32) + // NOTE: Cleanup the semaphore since we're detached, all left-over resources need to be cleaned + // up ourselves. + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + #endif DN_TCDeinit(&thread->context, DN_TCDeinitArenas_Yes); + } else { + #if !defined(DN_PLATFORM_WIN32) + DN_OS_SemaphoreIncrement(&thread->join_done_sem, 1); // NOTE: Signal for DN_OS_ThreadJoin waits on this. + #endif + } } static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context) @@ -12454,6 +12464,9 @@ static void DN_OS_ThreadPreInit_(DN_OSThread *thread, DN_OSThreadFunc *func, DN_ 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*/); +#if !defined(DN_PLATFORM_WIN32) + thread->join_done_sem = DN_OS_SemaphoreInit(0 /*initial_count*/); +#endif thread->tc_init_args = init_args.tc_args; thread->flags = init_args.flags; if (lane) { @@ -12479,8 +12492,12 @@ static void DN_OS_ThreadPostInit_(DN_OSThread *thread, bool result) 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) + if (!result) { + #if !defined(DN_PLATFORM_WIN32) + DN_OS_SemaphoreDeinit(&thread->join_done_sem); + #endif *thread = {}; + } } DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault() @@ -12598,10 +12615,10 @@ DN_API void DN_OS_ThreadLanewayDispatch(DN_OSThreadLaneway *laneway, DN_OSThread } } -DN_API void DN_OS_ThreadLanewayJoin(DN_OSThreadLaneway *laneway, DN_TCDeinitArenas deinit_arenas) +DN_API void DN_OS_ThreadLanewayJoin(DN_OSThreadLaneway *laneway, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas) { for (DN_ForItSize(it, DN_OSThread, laneway->threads, laneway->threads_count)) - DN_OS_ThreadJoin(it.data, deinit_arenas); + DN_OS_ThreadJoin(it.data, timeout_ms, deinit_arenas); DN_OS_BarrierDeinit(&laneway->barrier); } @@ -12685,7 +12702,7 @@ DN_API void DN_OS_AsyncDeinit(DN_OSAsyncCore *async) DN_AtomicSetValue32(&async->join_threads, true); DN_OS_SemaphoreIncrement(&async->worker_sem, async->thread_count); for (DN_ForItSize(it, DN_OSThread, async->threads, async->thread_count)) - DN_OS_ThreadJoin(it.data, DN_TCDeinitArenas_Yes); + DN_OS_ThreadJoin(it.data, UINT32_MAX, DN_TCDeinitArenas_Yes); } static bool DN_OS_AsyncQueueTask_(DN_OSAsyncCore *async, DN_OSAsyncTask const *task, DN_U64 wait_time_ms) { @@ -12727,7 +12744,7 @@ DN_API DN_OSAsyncTask DN_OS_AsyncQueueTask(DN_OSAsyncCore *async, DN_OSAsyncWork result.completion_sem = DN_OS_SemaphoreInit(0); result.queued = DN_OS_AsyncQueueTask_(async, &result, wait_time_ms); if (!result.queued) - DN_OS_SemaphoreDeinit(&result.completion_sem); + DN_OS_SemaphoreDeinit(&result.completion_sem); return result; } @@ -13772,7 +13789,7 @@ void DN_NET_CurlDeinit(DN_NETCore *net) DN_NETCurlCore *curl = DN_Cast(DN_NETCurlCore *) net->context; curl->kill_thread = true; curl_multi_wakeup(curl->thread_curlm); - DN_OS_ThreadJoin(&curl->thread, DN_TCDeinitArenas_Yes); + DN_OS_ThreadJoin(&curl->thread, UINT32_MAX, DN_TCDeinitArenas_Yes); } static DN_NETRequestHandle DN_NET_CurlDoRequest_(DN_NETCore *net, DN_Str8 url, DN_Str8 method, DN_NETDoHTTPArgs const *args, DN_NETRequestType type) diff --git a/Source/dn.h b/Source/dn.h index 5ba949c..3aa6dbc 100644 --- a/Source/dn.h +++ b/Source/dn.h @@ -2512,6 +2512,9 @@ struct DN_OSThread DN_OSThreadFunc *func; DN_OSSemaphore thread_exec_wait_for_thread_id_sem; DN_OSSemaphore caller_wait_for_thread_init_to_finish_sem; +#if !defined(DN_PLATFORM_WIN32) + DN_OSSemaphore join_done_sem; +#endif DN_TCInitArgs tc_init_args; }; @@ -4771,9 +4774,13 @@ DN_API void DN_OS_ConditionVariableBroadcast (D // // For the general use-case if you wish to use lanes then prefer the `DN_OS_ThreadLane` APIs // below which create the threads with the lane information setup accordingly. + +// DN_OS_ThreadJoin +// Returns true if the thread joined successfully or it is already joined. False if timed out +// waiting for the thread to join. DN_API bool DN_OS_ThreadInitLane (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadLane *lane, DN_OSThreadInitArgs init_args, void *user_context); DN_API bool DN_OS_ThreadInit (DN_OSThread *thread, DN_OSThreadFunc *func, DN_OSThreadInitArgs init_args, void *user_context); -DN_API bool DN_OS_ThreadJoin (DN_OSThread *thread, DN_TCDeinitArenas deinit_arenas); +DN_API bool DN_OS_ThreadJoin (DN_OSThread *thread, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas); DN_API DN_U32 DN_OS_ThreadID (); DN_API DN_OSThreadInitArgs DN_OS_ThreadInitArgsDefault (); DN_API void DN_OS_ThreadSetNameFmt (char const *fmt, ...); @@ -4847,7 +4854,7 @@ DN_API DN_V2USize DN_OS_ThreadLaneRange (D DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArgs (DN_OSThread* threads, DN_USize threads_count, DN_UPtr* shared_mem); DN_API DN_OSThreadLaneway DN_OS_ThreadLanewayFromArena (DN_USize threads_count, DN_Arena* arena); DN_API void DN_OS_ThreadLanewayDispatch (DN_OSThreadLaneway *laneway, DN_OSThreadFunc *entry_point, DN_OSThreadInitArgs init_args, void *user_context); -DN_API void DN_OS_ThreadLanewayJoin (DN_OSThreadLaneway *laneway, DN_TCDeinitArenas deinit_arenas); +DN_API void DN_OS_ThreadLanewayJoin (DN_OSThreadLaneway *laneway, DN_U32 timeout_ms, DN_TCDeinitArenas deinit_arenas); DN_API DN_OSThreadLane* DN_OS_TCThreadLane (); DN_API void DN_OS_TCThreadLaneSync (void **ptr_to_share);