From 705b78f53e11e4a29c4f9b58ca2d041bab8ecd3d Mon Sep 17 00:00:00 2001 From: mchughalex <25671999+mchughalex@users.noreply.github.com> Date: Tue, 21 Jul 2026 10:33:27 +0100 Subject: [PATCH] system: flush logs and _Exit on quit for macOS and Linux instead of tearing down under live guest threads --- include/rex/logging/api.h | 7 +++++++ src/core/logging.cpp | 10 ++++++++++ src/ui/windowed_app_main_posix.cpp | 5 ++++- src/ui/windowed_app_main_sdl.cpp | 18 ++++++++++++------ 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/include/rex/logging/api.h b/include/rex/logging/api.h index 5f84121..542ac35 100644 --- a/include/rex/logging/api.h +++ b/include/rex/logging/api.h @@ -71,6 +71,13 @@ void InitLoggingEarly(); */ void ShutdownLogging(); +/** + * Flush all loggers without shutting the logging system down. Safe to call + * while other threads may still be logging; intended for fast-exit paths + * that end the process with _Exit right after flushing. + */ +void FlushLogging(); + /** * Register a new log category at runtime. * diff --git a/src/core/logging.cpp b/src/core/logging.cpp index 4991aab..e5c89f6 100644 --- a/src/core/logging.cpp +++ b/src/core/logging.cpp @@ -292,6 +292,16 @@ void InitLogging(const char* log_file, spdlog::level::level_enum level) { InitLogging(config); } +void FlushLogging() { + std::lock_guard lock(g_mutex); + if (!g_initialized && !g_early_initialized) + return; + + for (auto& entry : g_registry) + if (entry.logger) + entry.logger->flush(); +} + void ShutdownLogging() { std::lock_guard lock(g_mutex); if (!g_initialized && !g_early_initialized) diff --git a/src/ui/windowed_app_main_posix.cpp b/src/ui/windowed_app_main_posix.cpp index ebabe7f..9d7750c 100644 --- a/src/ui/windowed_app_main_posix.cpp +++ b/src/ui/windowed_app_main_posix.cpp @@ -75,5 +75,8 @@ extern "C" int main(int argc_pre_gtk, char** argv_pre_gtk) { // Logging may still be needed in the destructors. rex::ShutdownLogging(); - return result; + // Guest threads that could not be cancelled may still be running; global + // static destruction under them can turn a clean quit into an abort or a + // nonzero exit status. Everything is flushed - end the process here. + std::_Exit(result); } diff --git a/src/ui/windowed_app_main_sdl.cpp b/src/ui/windowed_app_main_sdl.cpp index c4fc966..a8bfad9 100644 --- a/src/ui/windowed_app_main_sdl.cpp +++ b/src/ui/windowed_app_main_sdl.cpp @@ -46,16 +46,22 @@ int main(int argc, char** argv) { bool initialized = app->OnInitialize(); result = initialized ? app_context.RunMainLoop() : EXIT_FAILURE; +#if REX_PLATFORM_MAC + // Skip app/runtime teardown entirely: guest threads cannot be reliably + // stopped on Darwin (pthread_cancel only lands at cancellation points, + // never in CPU-bound recompiled code), so the destructor chain races + // still-running guest threads over freed kernel objects and turns a + // normal quit into the macOS crash-reporter dialog. Flush the logs and + // leave; the OS reclaims everything else. Flush-only on purpose - + // ShutdownLogging destroys sinks a straggler thread may still be using. + rex::FlushLogging(); + std::_Exit(result); +#else app->InvokeOnDestroy(); +#endif } rex::ShutdownLogging(); -#if REX_PLATFORM_MAC - // After explicit app teardown and log flushing, avoid macOS/SDL/global static - // teardown paths that can change a clean Close Game exit into status 1. - std::_Exit(result); -#else SDL_Quit(); -#endif return result; }