@@ -1391,108 +1391,51 @@ 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 ;
1395- GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &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 ;
13961396
1397- List<GDScript::UpdatableFuncPtrElement>::Element *GDScript::_add_func_ptr_to_update (GDScriptFunction **p_func_ptr_ptr) {
1398- MutexLock lock (func_ptrs_to_update_mutex);
1399-
1400- List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back (UpdatableFuncPtrElement ());
1397+ GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update (GDScriptFunction **p_func_ptr_ptr) {
1398+ UpdatableFuncPtrElement result = {};
14011399
14021400 {
1403- MutexLock lock2 (func_ptrs_to_update_thread_local. mutex );
1404- result-> get () .element = func_ptrs_to_update_thread_local. ptrs .push_back (p_func_ptr_ptr);
1405- result-> get (). 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;
14061404
1407- if (likely (func_ptrs_to_update_thread_local. initialized )) {
1405+ if (likely (func_ptrs_to_update_thread_local-> initialized )) {
14081406 return result;
14091407 }
14101408
1411- func_ptrs_to_update_thread_local. initialized = true ;
1409+ func_ptrs_to_update_thread_local-> initialized = true ;
14121410 }
14131411
1414- func_ptrs_to_update.push_back (&func_ptrs_to_update_thread_local);
1412+ MutexLock lock (func_ptrs_to_update_mutex);
1413+ func_ptrs_to_update.push_back (func_ptrs_to_update_thread_local);
1414+ func_ptrs_to_update_thread_local->rc ++;
14151415
14161416 return result;
14171417}
14181418
1419- void GDScript::_remove_func_ptr_to_update (List<UpdatableFuncPtrElement>::Element *p_func_ptr_element) {
1420- // None of these checks should ever fail, unless there's a bug.
1421- // They can be removed once we are sure they never catch anything.
1422- // Left here now due to extra safety needs late in the release cycle.
1423- ERR_FAIL_NULL (p_func_ptr_element);
1424- MutexLock lock (func_ptrs_to_update_thread_local.mutex );
1425- ERR_FAIL_NULL (p_func_ptr_element->get ().element );
1426- ERR_FAIL_NULL (p_func_ptr_element->get ().mutex );
1427- MutexLock lock2 (*p_func_ptr_element->get ().mutex );
1428- p_func_ptr_element->get ().element ->erase ();
1429- p_func_ptr_element->erase ();
1419+ void GDScript::_remove_func_ptr_to_update (const UpdatableFuncPtrElement &p_func_ptr_element) {
1420+ ERR_FAIL_NULL (p_func_ptr_element.element );
1421+ ERR_FAIL_NULL (p_func_ptr_element.func_ptr );
1422+ MutexLock lock (p_func_ptr_element.func_ptr ->mutex );
1423+ p_func_ptr_element.element ->erase ();
14301424}
14311425
14321426void GDScript::_fixup_thread_function_bookkeeping () {
14331427 // Transfer the ownership of these update items to the main thread,
14341428 // because the current one is dying, leaving theirs orphan, dangling.
14351429
1436- HashSet<GDScript *> scripts;
1437-
14381430 DEV_ASSERT (!Thread::is_main_thread ());
1439- MutexLock lock (func_ptrs_to_update_main_thread->mutex );
14401431
1441- {
1442- MutexLock lock2 (func_ptrs_to_update_thread_local.mutex );
1443-
1444- while (!func_ptrs_to_update_thread_local.ptrs .is_empty ()) {
1445- // Transfer the thread-to-script records from the dying thread to the main one.
1446-
1447- List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs .front ();
1448- List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs .push_front (E->get ());
1449-
1450- GDScript *script = (*E->get ())->get_script ();
1451- if (!scripts.has (script)) {
1452- scripts.insert (script);
1453-
1454- // Replace dying thread by the main thread in the script-to-thread records.
1455-
1456- MutexLock lock3 (script->func_ptrs_to_update_mutex );
1457- DEV_ASSERT (script->func_ptrs_to_update .find (&func_ptrs_to_update_thread_local));
1458- {
1459- for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems .front (); F; F = F->next ()) {
1460- bool is_dying_thread_entry = F->get ().mutex == &func_ptrs_to_update_thread_local.mutex ;
1461- if (is_dying_thread_entry) {
1462- // This may lead to multiple main-thread entries, but that's not a problem
1463- // and allows to reuse the element, which is needed, since it's tracked by pointer.
1464- F->get ().element = new_E;
1465- F->get ().mutex = &func_ptrs_to_update_main_thread->mutex ;
1466- }
1467- }
1468- }
1469- }
1432+ MutexLock lock (func_ptrs_to_update_main_thread.mutex );
1433+ MutexLock lock2 (func_ptrs_to_update_thread_local->mutex );
14701434
1471- E->erase ();
1472- }
1473- }
1474- func_ptrs_to_update_main_thread->initialized = true ;
1475-
1476- {
1477- // Remove orphan thread-to-script entries from every script.
1478- // FIXME: This involves iterating through every script whenever a thread dies.
1479- // While it's OK that thread creation/destruction are heavy operations,
1480- // additional bookkeeping can be used to outperform this brute-force approach.
1481-
1482- GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton ();
1483-
1484- MutexLock lock2 (gd_lang->mutex );
1485-
1486- for (SelfList<GDScript> *s = gd_lang->script_list .first (); s; s = s->next ()) {
1487- GDScript *script = s->self ();
1488- for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update .front (); E; E = E->next ()) {
1489- bool is_dying_thread_entry = &E->get ()->mutex == &func_ptrs_to_update_thread_local.mutex ;
1490- if (is_dying_thread_entry) {
1491- E->erase ();
1492- break ;
1493- }
1494- }
1495- }
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 ;
14961439 }
14971440}
14981441
@@ -1516,12 +1459,29 @@ void GDScript::clear(ClearData *p_clear_data) {
15161459 {
15171460 MutexLock outer_lock (func_ptrs_to_update_mutex);
15181461 for (UpdatableFuncPtr *updatable : func_ptrs_to_update) {
1519- MutexLock inner_lock (updatable->mutex );
1520- for (GDScriptFunction **func_ptr_ptr : updatable->ptrs ) {
1521- *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);
15221483 }
15231484 }
1524- func_ptrs_to_update_elems.clear ();
15251485 }
15261486
15271487 RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies ();
@@ -2141,8 +2101,25 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
21412101 named_globals.erase (p_name);
21422102}
21432103
2104+ void GDScriptLanguage::thread_enter () {
2105+ GDScript::func_ptrs_to_update_thread_local = memnew (GDScript::UpdatableFuncPtr);
2106+ }
2107+
21442108void GDScriptLanguage::thread_exit () {
21452109 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+ }
21462123}
21472124
21482125void GDScriptLanguage::init () {
@@ -2177,6 +2154,8 @@ void GDScriptLanguage::init() {
21772154 _add_global (E.name , E.ptr );
21782155 }
21792156
2157+ GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread;
2158+
21802159#ifdef TESTS_ENABLED
21812160 GDScriptTests::GDScriptTestRunner::handle_cmdline ();
21822161#endif
@@ -2226,6 +2205,8 @@ void GDScriptLanguage::finish() {
22262205 }
22272206 script_list.clear ();
22282207 function_list.clear ();
2208+
2209+ DEV_ASSERT (GDScript::func_ptrs_to_update_main_thread.rc == 1 );
22292210}
22302211
22312212void GDScriptLanguage::profiling_start () {
0 commit comments