more proper sys_panic

This commit is contained in:
jacob 2024-04-15 19:31:22 -05:00
parent 511364ee05
commit 228bef5a68
13 changed files with 134 additions and 66 deletions

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
}
/* ========================== *

View File

@ -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();

View File

@ -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

View File

@ -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);
}
/* ========================== *

View File

@ -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;

View File

@ -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);
/* ========================== *

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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);
}
}