From 17cd7dbac70a99fd63dfd23dec715179077582d4 Mon Sep 17 00:00:00 2001 From: Lukas Tenbrink Date: Wed, 15 Jul 2026 12:54:56 +0200 Subject: [PATCH] Make it impossible to have more than one main thread. (cherry picked from commit 2ff661cd8d9b229b6a8d0e64ccd9cb52e92c1315) --- core/os/thread.cpp | 14 ++++++++++++++ core/os/thread.h | 11 +++++------ drivers/apple/thread_apple.cpp | 14 ++++++++++++++ drivers/apple/thread_apple.h | 6 +++--- main/main.cpp | 11 +++++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 860f2218e5..164fb66880 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -88,6 +88,20 @@ void Thread::wait_to_finish() { id = UNASSIGNED_ID; } +void Thread::make_main_thread() { + if (caller_id == MAIN_ID) { + return; // We're already the main thread + } + CRASH_COND_MSG(!is_main_thread_assigned.set_if_clear(), "A second thread attempted to become the main thread."); + caller_id = MAIN_ID; +} + +void Thread::release_main_thread() { + CRASH_COND_MSG(caller_id != MAIN_ID, "Trying to release main thread from a thread that isn't main."); + CRASH_COND(!is_main_thread_assigned.clear_if_set()); + caller_id = id_counter.increment(); +} + Error Thread::set_name(const String &p_name) { if (platform_functions.set_name) { return platform_functions.set_name(p_name); diff --git a/core/os/thread.h b/core/os/thread.h index b4faf89ccf..f395e1e272 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -107,15 +107,13 @@ private: ID id = UNASSIGNED_ID; + static inline SafeFlag is_main_thread_assigned{ false }; static SafeNumeric id_counter; static thread_local ID caller_id; THREADING_NAMESPACE::thread thread; static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); - static void make_main_thread() { caller_id = MAIN_ID; } - static void release_main_thread() { caller_id = id_counter.increment(); } - public: static void _set_platform_functions(const PlatformFunctions &p_functions); @@ -130,6 +128,8 @@ public: _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } _FORCE_INLINE_ static bool is_main_thread() { return caller_id == MAIN_ID; } // Gain a tiny bit of perf here because there is no need to validate caller_id here, because only main thread will be set as 1. + static void make_main_thread(); + static void release_main_thread(); static Error set_name(const String &p_name); @@ -182,9 +182,6 @@ private: static PlatformFunctions platform_functions; - static void make_main_thread() {} - static void release_main_thread() {} - public: static void _set_platform_functions(const PlatformFunctions &p_functions); @@ -193,6 +190,8 @@ public: _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } _FORCE_INLINE_ static bool is_main_thread() { return true; } + static void make_main_thread() {} + static void release_main_thread() {} static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } diff --git a/drivers/apple/thread_apple.cpp b/drivers/apple/thread_apple.cpp index 2a35128988..655f66ccea 100644 --- a/drivers/apple/thread_apple.cpp +++ b/drivers/apple/thread_apple.cpp @@ -62,6 +62,20 @@ void *Thread::thread_callback(void *p_data) { return nullptr; } +void Thread::make_main_thread() { + if (caller_id == MAIN_ID) { + return; // We're already the main thread + } + CRASH_COND_MSG(!is_main_thread_assigned.set_if_clear(), "A second thread attempted to become the main thread."); + caller_id = MAIN_ID; +} + +void Thread::release_main_thread() { + CRASH_COND_MSG(caller_id != MAIN_ID, "Trying to release main thread from a thread that isn't main."); + CRASH_COND(!is_main_thread_assigned.clear_if_set()); + caller_id = id_counter.increment(); +} + Error Thread::set_name(const String &p_name) { int err = pthread_setname_np(p_name.utf8().get_data()); return err == 0 ? OK : ERR_INVALID_PARAMETER; diff --git a/drivers/apple/thread_apple.h b/drivers/apple/thread_apple.h index 729c56cc7c..b1f3d05da0 100644 --- a/drivers/apple/thread_apple.h +++ b/drivers/apple/thread_apple.h @@ -76,14 +76,12 @@ private: ID id = UNASSIGNED_ID; pthread_t pthread; + static inline SafeFlag is_main_thread_assigned{ false }; static SafeNumeric id_counter; static thread_local ID caller_id; static void *thread_callback(void *p_data); - static void make_main_thread() { caller_id = MAIN_ID; } - static void release_main_thread() { caller_id = id_counter.increment(); } - public: _FORCE_INLINE_ static void yield() { pthread_yield_np(); } @@ -96,6 +94,8 @@ public: _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } _FORCE_INLINE_ static bool is_main_thread() { return caller_id == MAIN_ID; } + static void make_main_thread(); + static void release_main_thread(); static Error set_name(const String &p_name); diff --git a/main/main.cpp b/main/main.cpp index b8a7c6c356..e83574b45f 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -745,6 +745,7 @@ void Main::test_cleanup() {} // are initialized here. This also combines `Main::setup2()` initialization. Error Main::test_setup() { Thread::make_main_thread(); + set_current_thread_safe_for_nodes(true); OS::get_singleton()->initialize(); @@ -884,6 +885,8 @@ Error Main::test_setup() { void Main::test_cleanup() { ERR_FAIL_COND(!_start_success); + Thread::make_main_thread(); + // Printing in the usual way can become problematic during/after cleanup. CoreGlobals::print_ready = false; @@ -971,6 +974,8 @@ void Main::test_cleanup() { unregister_core_types(); OS::get_singleton()->finalize_core(); + + Thread::release_main_thread(); } #endif // TESTS_ENABLED @@ -3004,6 +3009,8 @@ error: OS::get_singleton()->finalize_core(); locale = String(); + Thread::release_main_thread(); + return exit_err; } @@ -5210,6 +5217,8 @@ void Main::force_redraw() { * The order matters as some of those steps are linked with each other. */ void Main::cleanup(bool p_force) { + Thread::make_main_thread(); + GodotProfileZone("cleanup"); OS::get_singleton()->benchmark_begin_measure("Shutdown", "Main::Cleanup"); if (!p_force) { @@ -5395,4 +5404,6 @@ void Main::cleanup(bool p_force) { OS::get_singleton()->benchmark_dump(); OS::get_singleton()->finalize_core(); + + Thread::release_main_thread(); }