@@ -1391,36 +1391,54 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) {
13911391}
13921392#endif
13931393
1394- thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local;
1394+ GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread;
1395+ thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr ;
13951396
13961397GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update (GDScriptFunction **p_func_ptr_ptr) {
13971398 UpdatableFuncPtrElement result = {};
13981399
13991400 {
1400- MutexLock lock (func_ptrs_to_update_thread_local. mutex );
1401- result.element = func_ptrs_to_update_thread_local. ptrs .push_back (p_func_ptr_ptr);
1402- result.mutex = & func_ptrs_to_update_thread_local. mutex ;
1401+ MutexLock lock (func_ptrs_to_update_thread_local-> mutex );
1402+ result.element = func_ptrs_to_update_thread_local-> ptrs .push_back (p_func_ptr_ptr);
1403+ result.func_ptr = func_ptrs_to_update_thread_local;
14031404
1404- if (likely (func_ptrs_to_update_thread_local. initialized )) {
1405+ if (likely (func_ptrs_to_update_thread_local-> initialized )) {
14051406 return result;
14061407 }
14071408
1408- func_ptrs_to_update_thread_local. initialized = true ;
1409+ func_ptrs_to_update_thread_local-> initialized = true ;
14091410 }
14101411
14111412 MutexLock lock (func_ptrs_to_update_mutex);
1412- func_ptrs_to_update.push_back (&func_ptrs_to_update_thread_local);
1413+ func_ptrs_to_update.push_back (func_ptrs_to_update_thread_local);
1414+ func_ptrs_to_update_thread_local->rc ++;
14131415
14141416 return result;
14151417}
14161418
1417- void GDScript::_remove_func_ptr_to_update (const UpdatableFuncPtrElement p_func_ptr_element) {
1419+ void GDScript::_remove_func_ptr_to_update (const UpdatableFuncPtrElement & p_func_ptr_element) {
14181420 ERR_FAIL_NULL (p_func_ptr_element.element );
1419- ERR_FAIL_NULL (p_func_ptr_element.mutex );
1420- MutexLock lock (* p_func_ptr_element.mutex );
1421+ ERR_FAIL_NULL (p_func_ptr_element.func_ptr );
1422+ MutexLock lock (p_func_ptr_element.func_ptr -> mutex );
14211423 p_func_ptr_element.element ->erase ();
14221424}
14231425
1426+ void GDScript::_fixup_thread_function_bookkeeping () {
1427+ // Transfer the ownership of these update items to the main thread,
1428+ // because the current one is dying, leaving theirs orphan, dangling.
1429+
1430+ DEV_ASSERT (!Thread::is_main_thread ());
1431+
1432+ MutexLock lock (func_ptrs_to_update_main_thread.mutex );
1433+ MutexLock lock2 (func_ptrs_to_update_thread_local->mutex );
1434+
1435+ while (!func_ptrs_to_update_thread_local->ptrs .is_empty ()) {
1436+ List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs .front ();
1437+ E->transfer_to_back (&func_ptrs_to_update_main_thread.ptrs );
1438+ func_ptrs_to_update_thread_local->transferred = true ;
1439+ }
1440+ }
1441+
14241442void GDScript::clear (ClearData *p_clear_data) {
14251443 if (clearing) {
14261444 return ;
@@ -1441,9 +1459,27 @@ void GDScript::clear(ClearData *p_clear_data) {
14411459 {
14421460 MutexLock outer_lock (func_ptrs_to_update_mutex);
14431461 for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
1444- MutexLock inner_lock (updatable->mutex );
1445- for (GDScriptFunction **func_ptr_ptr : updatable->ptrs ) {
1446- *func_ptr_ptr = nullptr ;
1462+ bool destroy = false ;
1463+ {
1464+ MutexLock inner_lock (updatable->mutex );
1465+ if (updatable->transferred ) {
1466+ func_ptrs_to_update_main_thread.mutex .lock ();
1467+ }
1468+ for (GDScriptFunction **func_ptr_ptr : updatable->ptrs ) {
1469+ *func_ptr_ptr = nullptr ;
1470+ }
1471+ DEV_ASSERT (updatable->rc != 0 );
1472+ updatable->rc --;
1473+ if (updatable->rc == 0 ) {
1474+ destroy = true ;
1475+ }
1476+ if (updatable->transferred ) {
1477+ func_ptrs_to_update_main_thread.mutex .unlock ();
1478+ }
1479+ }
1480+ if (destroy) {
1481+ DEV_ASSERT (updatable != &func_ptrs_to_update_main_thread);
1482+ memdelete (updatable);
14471483 }
14481484 }
14491485 }
@@ -2065,6 +2101,27 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
20652101 named_globals.erase (p_name);
20662102}
20672103
2104+ void GDScriptLanguage::thread_enter () {
2105+ GDScript::func_ptrs_to_update_thread_local = memnew (GDScript::UpdatableFuncPtr);
2106+ }
2107+
2108+ void GDScriptLanguage::thread_exit () {
2109+ GDScript::_fixup_thread_function_bookkeeping ();
2110+
2111+ bool destroy = false ;
2112+ {
2113+ MutexLock lock (GDScript::func_ptrs_to_update_thread_local->mutex );
2114+ DEV_ASSERT (GDScript::func_ptrs_to_update_thread_local->rc != 0 );
2115+ GDScript::func_ptrs_to_update_thread_local->rc --;
2116+ if (GDScript::func_ptrs_to_update_thread_local->rc == 0 ) {
2117+ destroy = true ;
2118+ }
2119+ }
2120+ if (destroy) {
2121+ memdelete (GDScript::func_ptrs_to_update_thread_local);
2122+ }
2123+ }
2124+
20682125void GDScriptLanguage::init () {
20692126 // populate global constants
20702127 int gcc = CoreConstants::get_global_constant_count ();
@@ -2097,6 +2154,8 @@ void GDScriptLanguage::init() {
20972154 _add_global (E.name , E.ptr );
20982155 }
20992156
2157+ GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread;
2158+
21002159#ifdef TESTS_ENABLED
21012160 GDScriptTests::GDScriptTestRunner::handle_cmdline ();
21022161#endif
@@ -2146,6 +2205,8 @@ void GDScriptLanguage::finish() {
21462205 }
21472206 script_list.clear ();
21482207 function_list.clear ();
2208+
2209+ DEV_ASSERT (GDScript::func_ptrs_to_update_main_thread.rc == 1 );
21492210}
21502211
21512212void GDScriptLanguage::profiling_start () {
0 commit comments