4040#include < godot_cpp/godot.hpp>
4141
4242#if defined(MACOS_ENABLED) && defined(HOT_RELOAD_ENABLED)
43+ #include < map>
4344#include < mutex>
44- #define _GODOT_CPP_AVOID_THREAD_LOCAL
45- #define _GODOT_CPP_THREAD_LOCAL
46- #else
47- #define _GODOT_CPP_THREAD_LOCAL thread_local
45+ #include < thread>
4846#endif
4947
5048namespace godot {
@@ -65,21 +63,45 @@ class Wrapped {
6563 template <typename T, std::enable_if_t <std::is_base_of<::godot::Wrapped, T>::value, bool >>
6664 friend _ALWAYS_INLINE_ void _pre_initialize ();
6765
68- #ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
69- static std::recursive_mutex _constructing_mutex;
66+ struct ConstructInfo {
67+ const StringName *extension_class_name = nullptr ;
68+ const GDExtensionInstanceBindingCallbacks *class_binding_callbacks = nullptr ;
69+ #ifdef HOT_RELOAD_ENABLED
70+ GDExtensionObjectPtr recreate_owner = nullptr ;
7071#endif
72+ };
7173
72- _GODOT_CPP_THREAD_LOCAL static const StringName *_constructing_extension_class_name;
73- _GODOT_CPP_THREAD_LOCAL static const GDExtensionInstanceBindingCallbacks *_constructing_class_binding_callbacks;
74-
75- #ifdef HOT_RELOAD_ENABLED
76- _GODOT_CPP_THREAD_LOCAL static GDExtensionObjectPtr _constructing_recreate_owner;
74+ #if defined(MACOS_ENABLED) && defined(HOT_RELOAD_ENABLED)
75+ // On macOS, `thread_local` storage keeps the library from being unloaded,
76+ // which breaks hot-reload. Instead we keep each thread's `ConstructInfo` in a
77+ // map keyed by thread id.
78+ class ConstructInfoStore {
79+ std::mutex mutex;
80+ std::map<std::thread::id, ConstructInfo> infos;
81+
82+ public:
83+ ConstructInfo &get () {
84+ std::lock_guard<std::mutex> lock (mutex);
85+ return infos[std::this_thread::get_id ()];
86+ }
87+ };
88+
89+ static ConstructInfo &_get_construct_info () {
90+ static ConstructInfoStore store;
91+ return store.get ();
92+ }
93+ #else
94+ static ConstructInfo &_get_construct_info () {
95+ static thread_local ConstructInfo info;
96+ return info;
97+ }
7798#endif
7899
79100 template <typename T>
80101 _ALWAYS_INLINE_ static void _set_construct_info () {
81- _constructing_extension_class_name = T::_get_extension_class_name ();
82- _constructing_class_binding_callbacks = &T::_gde_binding_callbacks;
102+ ConstructInfo &info = _get_construct_info ();
103+ info.extension_class_name = T::_get_extension_class_name ();
104+ info.class_binding_callbacks = &T::_gde_binding_callbacks;
83105 }
84106
85107protected:
@@ -124,9 +146,6 @@ class Wrapped {
124146
125147template <typename T, std::enable_if_t <std::is_base_of<::godot::Wrapped, T>::value, bool >>
126148_ALWAYS_INLINE_ void _pre_initialize () {
127- #ifdef _GODOT_CPP_AVOID_THREAD_LOCAL
128- Wrapped::_constructing_mutex.lock ();
129- #endif
130149 Wrapped::_set_construct_info<T>();
131150}
132151
0 commit comments