From 228bef5a68c6e21eb8e0a8f45e02c6970cf58b59 Mon Sep 17 00:00:00 2001 From: jacob Date: Mon, 15 Apr 2024 19:31:22 -0500 Subject: [PATCH] more proper sys_panic --- src/app.c | 4 +- src/app.h | 3 + src/arena.c | 8 +-- src/game.c | 4 +- src/log.c | 14 ++++- src/log.h | 4 ++ src/playback_wasapi.c | 4 +- src/scratch.h | 2 +- src/sys.h | 9 +-- src/sys_win32.c | 134 +++++++++++++++++++++++++++++------------- src/thread_local.c | 2 +- src/user.c | 8 +-- src/work.c | 4 +- 13 files changed, 134 insertions(+), 66 deletions(-) diff --git a/src/app.c b/src/app.c index 77fa1612..adfa34ef 100644 --- a/src/app.c +++ b/src/app.c @@ -235,11 +235,11 @@ void app_entry_point(void) { /* Start callback threads */ for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) { - callback->thread = sys_thread_init(&exit_callback_thread_entry_point, callback, STR("[P4] Exit callback thread")); + callback->thread = sys_thread_alloc(&exit_callback_thread_entry_point, callback, STR("[P4] Exit callback thread")); } /* Wait on callback threads */ for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) { - sys_thread_join(&callback->thread); + sys_thread_wait_release(&callback->thread); } } sys_mutex_unlock(&G.exit_callbacks_mutex); diff --git a/src/app.h b/src/app.h index 64dec18c..2ea537f0 100644 --- a/src/app.h +++ b/src/app.h @@ -5,9 +5,12 @@ typedef APP_EXIT_CALLBACK_FUNC_DEF(app_exit_callback_func); struct string app_write_path_cat(struct arena *arena, struct string filename); + /* Register a function that will be called when the application exits */ void app_register_exit_callback(app_exit_callback_func *func); + void app_entry_point(void); + void app_exit(void); #endif diff --git a/src/arena.c b/src/arena.c index 9fccb6dc..435aedcd 100644 --- a/src/arena.c +++ b/src/arena.c @@ -21,7 +21,7 @@ struct arena arena_alloc(u64 reserve) arena.base = sys_memory_reserve(reserve); if (!arena.base) { /* Hard fail on memory reserve failure for now */ - sys_panic_raw("Failed to reserve memory"); + sys_panic(STR("Failed to reserve memory")); } arena.reserved = reserve; @@ -31,7 +31,7 @@ struct arena arena_alloc(u64 reserve) ASAN_POISON(arena.base, ARENA_BLOCK_SIZE); if (!arena.base) { /* Hard fail on commit failure */ - sys_panic_raw("Failed to commit initial memory block: System may be out of memory"); + sys_panic(STR("Failed to commit initial memory block: System may be out of memory")); } arena.committed = ARENA_BLOCK_SIZE; @@ -68,12 +68,12 @@ void *_arena_push_bytes(struct arena *arena, u64 size, u64 align) u64 new_capacity = arena->committed + commit_bytes; if (new_capacity > arena->reserved) { /* Hard fail if we overflow reserved memory for now */ - sys_panic_raw("Failed to commit new memory block: Overflow of reserved memory"); + sys_panic(STR("Failed to commit new memory block: Overflow of reserved memory")); } void *commit_address = arena->base + arena->committed; if (!sys_memory_commit(commit_address, commit_bytes)) { /* Hard fail on memory allocation failure for now */ - sys_panic_raw("Failed to commit new memory block: System may be out of memory"); + sys_panic(STR("Failed to commit new memory block: System may be out of memory")); } __proffree(arena->base); __profalloc(arena->base, arena->committed + commit_bytes); diff --git a/src/game.c b/src/game.c index d9aa828f..f453f212 100644 --- a/src/game.c +++ b/src/game.c @@ -53,7 +53,7 @@ struct game_startup_receipt game_startup(struct mixer_startup_receipt *mixer_sr, G.published_tick_mutex = sys_mutex_alloc(); G.world.timescale = GAME_TIMESCALE; - G.game_thread = sys_thread_init(&game_thread_entry_point, NULL, STR("[P2] Game thread")); + G.game_thread = sys_thread_alloc(&game_thread_entry_point, NULL, STR("[P2] Game thread")); app_register_exit_callback(&game_shutdown); return (struct game_startup_receipt) { 0 }; @@ -63,7 +63,7 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(game_shutdown) { __prof; atomic_i32_eval_exchange(&G.game_thread_shutdown, true); - sys_thread_join(&G.game_thread); + sys_thread_wait_release(&G.game_thread); } /* ========================== * diff --git a/src/log.c b/src/log.c index bcf061a7..a201f0eb 100644 --- a/src/log.c +++ b/src/log.c @@ -102,6 +102,18 @@ INTERNAL void append_to_logfile(struct string msg) } } +/* Panic log function is separate to enforce zero side effects other than + * writing to log file. */ +void _log_panic(struct string msg) +{ + if (!atomic_i32_eval(&G.initialized)) { return; } + + if (G.file_valid) { + sys_file_write(G.file, BUFFER_FROM_STRING(STR("** PANICKING **\n"))); + sys_file_write(G.file, BUFFER_FROM_STRING(msg)); + } +} + #if LOG_INCLUDE_SOURCE_LOCATION void _log(i32 level, struct string file, u32 line, struct string msg) #else @@ -112,7 +124,7 @@ void _log(i32 level, struct string msg) if (!atomic_i32_eval(&G.initialized)) { return; } if (level < 0 || level >= LOG_LEVEL_COUNT) { - sys_panic_raw("Invalid log level"); + sys_panic(STR("Invalid log level")); } struct temp_arena scratch = scratch_begin_no_conflict(); diff --git a/src/log.h b/src/log.h index 1a5b1f06..80553e86 100644 --- a/src/log.h +++ b/src/log.h @@ -54,6 +54,8 @@ void log_register_callback(log_event_callback_func *func); * Logging macros * ========================== */ +#define log_panic(msg) _log_panic(msg) + #if LOG_LEVEL(LOG_LEVEL_CRITICAL) # if LOG_INCLUDE_SOURCE_LOCATION # define log_critical(msg) _log(LOG_LEVEL_CRITICAL, STR(__FILE__), __LINE__, msg) @@ -127,6 +129,8 @@ void log_register_callback(log_event_callback_func *func); struct log_startup_receipt { i32 _; }; struct log_startup_receipt log_startup(struct string logfile_path); +void _log_panic(struct string msg); + #if LOG_INCLUDE_SOURCE_LOCATION void _log(i32 level, struct string file, u32 line, struct string msg); #else diff --git a/src/playback_wasapi.c b/src/playback_wasapi.c index 7f70e8a2..af19e91f 100644 --- a/src/playback_wasapi.c +++ b/src/playback_wasapi.c @@ -59,7 +59,7 @@ struct playback_startup_receipt playback_startup(struct mixer_startup_receipt *m (UNUSED)mixer_sr; wasapi_initialize(); - G.playback_thread = sys_thread_init(&playback_thread_entry_point, NULL, STR("[P3] Audio thread")); + G.playback_thread = sys_thread_alloc(&playback_thread_entry_point, NULL, STR("[P3] Audio thread")); app_register_exit_callback(&playback_shutdown); return (struct playback_startup_receipt) { 0 }; @@ -69,7 +69,7 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(playback_shutdown) { __prof; atomic_i32_eval_exchange(&G.playback_thread_shutdown, true); - sys_thread_join(&G.playback_thread); + sys_thread_wait_release(&G.playback_thread); } /* ========================== * diff --git a/src/scratch.h b/src/scratch.h index c123c46b..a4c6fed1 100644 --- a/src/scratch.h +++ b/src/scratch.h @@ -32,7 +32,7 @@ INLINE void scratch_dbg_push(struct scratch_ctx *ctx, struct temp_arena *temp) { #if RTC if (ctx->scratch_id_stack_count >= ARRAY_COUNT(ctx->scratch_id_stack)) { - sys_panic_raw("Max debug scratch depth reached"); + sys_panic(STR("Max debug scratch depth reached")); } temp->scratch_id = ctx->next_scratch_id++; ctx->scratch_id_stack[ctx->scratch_id_stack_count++] = temp->scratch_id; diff --git a/src/sys.h b/src/sys.h index 3ca0d354..a5e11538 100644 --- a/src/sys.h +++ b/src/sys.h @@ -401,14 +401,14 @@ struct sys_thread { }; /* Creates a new thread running in the supplied `entry_point` */ -struct sys_thread sys_thread_init( +struct sys_thread sys_thread_alloc( sys_thread_entry_point_func *entry_point, void *thread_data, /* Passed as arg to `entry_point` */ struct string thread_name ); /* Halt and wait for a thread to finish */ -void sys_thread_join(struct sys_thread *thread); +void sys_thread_wait_release(struct sys_thread *thread); /* Gets the current executing thread's ID */ u32 sys_thread_id(void); @@ -472,11 +472,6 @@ void sys_exit(void); u32 sys_rand_u32(void); -/* Implementation of this function should have minimal side effects (e.g. avoid - * memory allocation), since it may be called in circumstances where those side - * effects are non-functioning. */ -void sys_panic_raw(char *msg_cstr); - void sys_panic(struct string msg); /* ========================== * diff --git a/src/sys_win32.c b/src/sys_win32.c index 6eee83f1..49c754a8 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -102,6 +102,11 @@ GLOBAL struct { i32 scheduler_period_ms; DWORD thread_tls_index; + /* Panic */ + struct atomic_i32 panicking; + wchar_t panic_wstr[4096]; + HANDLE panic_event; + /* Lookup tables */ enum sys_btn vk_btn_table[256]; @@ -405,7 +410,7 @@ void sys_file_write(struct sys_file file, struct buffer data) sys_panic(string_format(scratch.arena, STR("Tried to write too many bytes to disk (%F)"), FMT_UINT(data.size))); - //scratch_end(scratch); + scratch_end(scratch); } /* WriteFile returns TRUE on success */ @@ -598,7 +603,7 @@ INTERNAL HWND win32_create_window(struct win32_window *window) return hwnd; } -INTERNAL void window_thread_entry_point(void *arg) +INTERNAL SYS_THREAD_ENTRY_POINT_FUNC_DEF(window_thread_entry_point, arg) { struct win32_window *window = (struct win32_window *)arg; @@ -701,7 +706,7 @@ INTERNAL struct win32_window *win32_window_alloc(void) window->event_callbacks_mutex = sys_mutex_alloc(); /* Start window thread for processing events */ - window->event_thread = sys_thread_init(&window_thread_entry_point, window, STR("[P8] Window thread")); + window->event_thread = sys_thread_alloc(&window_thread_entry_point, window, STR("[P8] Window thread")); /* Wait for event thread to create actual window */ sync_flag_wait(&window->ready_sf); @@ -718,7 +723,7 @@ INTERNAL void win32_window_release(struct win32_window *window) /* Stop window thread */ window->event_thread_shutdown = true; - sys_thread_join(&window->event_thread); + sys_thread_wait_release(&window->event_thread); /* Release mutexes */ sys_mutex_release(&window->event_callbacks_mutex); @@ -1518,10 +1523,13 @@ INTERNAL struct win32_thread *win32_thread_alloc_assume_locked(void) t = arena_push_zero(&G.threads_arena, struct win32_thread); t->gen = 1; } - if (G.threads_last) { + if (!G.threads_first) { + G.threads_first = t; + } else { G.threads_last->next = t; } t->prev = G.threads_last; + G.threads_last = t; return t; } @@ -1606,7 +1614,7 @@ INTERNAL DWORD WINAPI win32_thread_proc(LPVOID vt) return 0; } -struct sys_thread sys_thread_init(sys_thread_entry_point_func *entry_point, void *thread_data, struct string thread_name) +struct sys_thread sys_thread_alloc(sys_thread_entry_point_func *entry_point, void *thread_data, struct string thread_name) { ASSERT(entry_point != NULL); logf_info("Creating thread \"%F\"", FMT_STR(thread_name)); @@ -1641,7 +1649,7 @@ struct sys_thread sys_thread_init(sys_thread_entry_point_func *entry_point, void return res; } -void sys_thread_join(struct sys_thread *thread) +void sys_thread_wait_release(struct sys_thread *thread) { HANDLE handle = 0; @@ -1660,8 +1668,8 @@ void sys_thread_join(struct sys_thread *thread) DWORD res = WaitForSingleObject(handle, INFINITE); CloseHandle(handle); - (UNUSED)res; ASSERT(res != WAIT_FAILED); + (UNUSED)res; } } @@ -1684,32 +1692,33 @@ void sys_thread_assert(u32 tid) void sys_message_box(enum sys_message_box_kind kind, struct string message) { struct temp_arena scratch = scratch_begin_no_conflict(); - wchar_t *message_wstr = wstr_from_string(scratch.arena, message); + wchar_t *message_wstr = wstr_from_string(scratch.arena, message); const wchar_t *title = L""; - UINT mbox_type = 0; + UINT mbox_type = MB_SETFOREGROUND; switch (kind) { case SYS_MESSAGE_BOX_KIND_OK: { - mbox_type = MB_ICONINFORMATION; + mbox_type |= MB_ICONINFORMATION; } break; case SYS_MESSAGE_BOX_KIND_WARNING: { title = L"Warning"; - mbox_type = MB_ICONWARNING; + mbox_type |= MB_ICONWARNING; } break; case SYS_MESSAGE_BOX_KIND_ERROR: { title = L"Error"; - mbox_type = MB_ICONERROR; + mbox_type |= MB_ICONERROR; } break; case SYS_MESSAGE_BOX_KIND_FATAL: { title = L"Fatal error"; - mbox_type = MB_ICONSTOP; + mbox_type |= MB_ICONSTOP; } break; } + logf_info("Showing message box: \"%F\"", FMT_STR(message)); MessageBoxExW(NULL, message_wstr, title, mbox_type, 0); scratch_end(scratch); @@ -1816,31 +1825,41 @@ u32 sys_rand_u32(void) return v; } -INTERNAL void panic_exit(void) -{ - ASSERT(false); - sys_exit(); -} - -void sys_panic_raw(char *msg_cstr) -{ - /* FIXME: Exit other threads before showing message box */ - MessageBoxExA(NULL, msg_cstr, "Fatal error", MB_ICONSTOP, 0); - panic_exit(); -} - void sys_panic(struct string msg) { - /* FIXME: Exit other threads before showing message box */ - logf_critical("Panicking: %F", FMT_STR(msg)); - struct temp_arena scratch = scratch_begin_no_conflict(); - msg = string_cat(scratch.arena, - STR("A fatal error has occured and the application needs to exit:\n\n"), - msg); - wchar_t *msg_wstr = wstr_from_string(scratch.arena, msg); - MessageBoxExW(NULL, msg_wstr, L"Fatal error", MB_ICONSTOP, 0); - panic_exit(); - scratch_end(scratch); + if (atomic_i32_eval_compare_exchange(&G.panicking, 0, 1) == 0) { + log_panic(msg); + + wchar_t *wstr = G.panic_wstr; + u64 wstr_len = 0; + + wchar_t prefix[] = L"A fatal error has occured and the application needs to exit:\n\n"; + MEMCPY(wstr, prefix, min_u64(ARRAY_COUNT(G.panic_wstr), (ARRAY_COUNT(prefix) << 1))); + wstr_len += ARRAY_COUNT(prefix) - 1; + + struct string str8 = msg; + u64 pos8 = 0; + while (pos8 < str8.len) { + struct string str8_remaining = { .len = (str8.len - pos8), .text = str8.text + pos8 }; + struct uni_decode_utf8_result decoded = uni_decode_utf8(str8_remaining); + struct uni_encode_utf16_result encoded = uni_encode_utf16(decoded.codepoint); + + u64 wstr_new_len = wstr_len + encoded.count16; + if (wstr_new_len < (ARRAY_COUNT(G.panic_wstr) - 1)) { + u16 *dest = wstr + wstr_len; + MEMCPY(dest, encoded.chars16, (encoded.count16 << 1)); + wstr_len = wstr_new_len; + pos8 += decoded.advance8; + } else { + break; + } + } + + wstr[wstr_len] = 0; + + WRITE_BARRIER(); + SetEvent(G.panic_event); + } } /* ========================== * @@ -1978,6 +1997,9 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, SetThreadDescription(GetCurrentThread(), L"Main thread"); + /* Set up panic event */ + G.panic_event = CreateEventW(NULL, true, false, NULL); + /* Query system info */ GetSystemInfo(&G.info); QueryPerformanceFrequency(&G.timer_frequency); @@ -2062,8 +2084,40 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, win32_thread_set_tls(&main_thread_tls); /* Call app thread and wait for return */ - struct sys_thread app_thread = sys_thread_init(&app_thread_entry_point, NULL, STR("[P9] App thread")); - sys_thread_join(&app_thread); + { + struct sys_thread app_thread = sys_thread_alloc(&app_thread_entry_point, NULL, STR("[P9] App thread")); + + HANDLE app_thread_handle = 0; + sys_rw_mutex_lock_shared(&G.threads_rw_mutex); + { + struct win32_thread *wt = win32_thread_from_sys_thread_assume_locked(app_thread); + app_thread_handle = wt->handle; + } + sys_rw_mutex_unlock_shared(&G.threads_rw_mutex); + + /* Wait for thread exit or panic */ + if (app_thread_handle) { + HANDLE wait_handles[] = { + app_thread_handle, + G.panic_event + }; + DWORD res = WaitForMultipleObjects(ARRAY_COUNT(wait_handles), wait_handles, false, INFINITE); + ASSERT(res != WAIT_FAILED); + (UNUSED)res; + } + } + + /* Panic message was set */ + if (atomic_i32_eval(&G.panicking)) { + /* Force stop threads */ + for (struct win32_thread *t = G.threads_last; t; t = t->prev) { + HANDLE handle = t->handle; + TerminateThread(handle, 0); + CloseHandle(handle); + } + error_msg = G.panic_wstr; + goto abort; + } /* Release main thread context */ win32_tls_release(&main_thread_tls); @@ -2075,7 +2129,7 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance, abort: if (error_msg) { - MessageBoxExW(NULL, error_msg, L"Fatal initialization error", MB_ICONSTOP, 0); + MessageBoxExW(NULL, error_msg, L"Fatal error", MB_ICONSTOP | MB_SETFOREGROUND, 0); ASSERT(false); return 1; } diff --git a/src/thread_local.c b/src/thread_local.c index 3da16d6c..36d5839f 100644 --- a/src/thread_local.c +++ b/src/thread_local.c @@ -72,7 +72,7 @@ void *_thread_local_eval(struct thread_local_var_meta *meta) if (id_plus_one == 0) { id = G.metas_count++; if (id >= MAX_THREAD_LOCAL_VARS) { - sys_panic_raw("Maximum number of thread local variables reached"); + sys_panic(STR("Maximum number of thread local variables reached")); } atomic_u64_eval_exchange(&meta->id_plus_one, id + 1); G.metas[id] = *meta; diff --git a/src/user.c b/src/user.c index 7699ff1a..521ba38e 100644 --- a/src/user.c +++ b/src/user.c @@ -133,7 +133,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.window = window; sys_window_register_event_callback(G.window, &window_event_callback); - G.user_thread = sys_thread_init(&user_thread_entry_point, NULL, STR("[P1] User thread")); + G.user_thread = sys_thread_alloc(&user_thread_entry_point, NULL, STR("[P1] User thread")); app_register_exit_callback(&user_shutdown); return (struct user_startup_receipt) { 0 }; @@ -143,7 +143,7 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(user_shutdown) { __prof; atomic_i32_eval_exchange(&G.user_thread_shutdown, true); - sys_thread_join(&G.user_thread); + sys_thread_wait_release(&G.user_thread); } /* ========================== * @@ -2175,7 +2175,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, G.window = window; sys_window_register_event_callback(G.window, &window_event_callback); - G.user_thread = sys_thread_init(&user_thread_entry_point, NULL, STR("[P1] User thread")); + G.user_thread = sys_thread_alloc(&user_thread_entry_point, NULL, STR("[P1] User thread")); return (struct user_startup_receipt) { 0 }; } @@ -2183,7 +2183,7 @@ struct user_startup_receipt user_startup(struct work_startup_receipt *work_sr, void user_shutdown(void) { G.shutdown = true; - sys_thread_join(&G.user_thread); + sys_thread_wait_release(&G.user_thread); } #endif diff --git a/src/work.c b/src/work.c index 321da11a..07ebaa37 100644 --- a/src/work.c +++ b/src/work.c @@ -132,7 +132,7 @@ struct work_startup_receipt work_startup(u32 num_worker_threads) FMT_UINT(i)); struct worker *worker = arena_push_zero(&G.arena, struct worker); - worker->thread = sys_thread_init(&worker_thread_entry_point, NULL, thread_name); + worker->thread = sys_thread_alloc(&worker_thread_entry_point, NULL, thread_name); if (prev) { prev->next = worker; } else { @@ -155,7 +155,7 @@ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(work_shutdown) atomic_i32_eval_exchange(&G.workers_shutdown, true); sys_semaphore_signal(&G.semaphore, G.worker_count); for (struct worker *worker = G.worker_head; (worker = worker->next);) { - sys_thread_join(&worker->thread); + sys_thread_wait_release(&worker->thread); } }