diff options
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r-- | modules/gdscript/gdscript.cpp | 142 |
1 files changed, 136 insertions, 6 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index a0213f05dd..4accdb4d21 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -278,6 +278,7 @@ struct _GDScriptMemberSort { void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { placeholders.erase(p_placeholder); } + #endif void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const { @@ -468,7 +469,7 @@ String GDScript::get_class_icon_path() const { } #endif -bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) { +bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update, bool p_base_exports_changed) { #ifdef TOOLS_ENABLED static Vector<GDScript *> base_caches; @@ -477,7 +478,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc } base_caches.append(this); - bool changed = false; + bool changed = p_base_exports_changed; if (source_changed_cache) { source_changed_cache = false; @@ -604,9 +605,15 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc void GDScript::update_exports() { #ifdef TOOLS_ENABLED + _update_exports_down(false); +#endif +} +#ifdef TOOLS_ENABLED +void GDScript::_update_exports_down(bool p_base_exports_changed) { bool cyclic_error = false; - _update_exports(&cyclic_error); + bool changed = _update_exports(&cyclic_error, false, nullptr, p_base_exports_changed); + if (cyclic_error) { return; } @@ -616,14 +623,14 @@ void GDScript::update_exports() { for (const ObjectID &E : copy) { Object *id = ObjectDB::get_instance(E); GDScript *s = Object::cast_to<GDScript>(id); + if (!s) { continue; } - s->update_exports(); + s->_update_exports_down(p_base_exports_changed || changed); } - -#endif } +#endif String GDScript::_get_debug_path() const { if (is_built_in() && !get_name().is_empty()) { @@ -985,6 +992,7 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { String old_path = path; path = p_path; + path_valid = true; GDScriptCache::move_script(old_path, p_path); for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) { @@ -993,6 +1001,9 @@ void GDScript::set_path(const String &p_path, bool p_take_over) { } String GDScript::get_script_path() const { + if (!path_valid && !get_path().is_empty()) { + return get_path(); + } return path; } @@ -1028,6 +1039,7 @@ Error GDScript::load_source_code(const String &p_path) { source = s; path = p_path; + path_valid = true; #ifdef TOOLS_ENABLED source_changed_cache = true; set_edited(false); @@ -1379,6 +1391,109 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) { } #endif +thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local; +GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_main_thread = &func_ptrs_to_update_thread_local; + +GDScript::UpdatableFuncPtrElement *GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) { + MutexLock lock(func_ptrs_to_update_mutex); + + List<UpdatableFuncPtrElement>::Element *result = func_ptrs_to_update_elems.push_back(UpdatableFuncPtrElement()); + + { + MutexLock lock2(func_ptrs_to_update_thread_local.mutex); + result->get().element = func_ptrs_to_update_thread_local.ptrs.push_back(p_func_ptr_ptr); + result->get().mutex = &func_ptrs_to_update_thread_local.mutex; + + if (likely(func_ptrs_to_update_thread_local.initialized)) { + return &result->get(); + } + + func_ptrs_to_update_thread_local.initialized = true; + } + + func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local); + + return &result->get(); +} + +void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement *p_func_ptr_element) { + // None of these checks should ever fail, unless there's a bug. + // They can be removed once we are sure they never catch anything. + // Left here now due to extra safety needs late in the release cycle. + ERR_FAIL_NULL(p_func_ptr_element); + MutexLock lock(*p_func_ptr_element->mutex); + ERR_FAIL_NULL(p_func_ptr_element->element); + ERR_FAIL_NULL(p_func_ptr_element->mutex); + p_func_ptr_element->element->erase(); +} + +void GDScript::_fixup_thread_function_bookkeeping() { + // Transfer the ownership of these update items to the main thread, + // because the current one is dying, leaving theirs orphan, dangling. + + HashSet<GDScript *> scripts; + + DEV_ASSERT(!Thread::is_main_thread()); + MutexLock lock(func_ptrs_to_update_main_thread->mutex); + + { + MutexLock lock2(func_ptrs_to_update_thread_local.mutex); + + while (!func_ptrs_to_update_thread_local.ptrs.is_empty()) { + // Transfer the thread-to-script records from the dying thread to the main one. + + List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local.ptrs.front(); + List<GDScriptFunction **>::Element *new_E = func_ptrs_to_update_main_thread->ptrs.push_front(E->get()); + + GDScript *script = (*E->get())->get_script(); + if (!scripts.has(script)) { + scripts.insert(script); + + // Replace dying thread by the main thread in the script-to-thread records. + + MutexLock lock3(script->func_ptrs_to_update_mutex); + DEV_ASSERT(script->func_ptrs_to_update.find(&func_ptrs_to_update_thread_local)); + { + for (List<UpdatableFuncPtrElement>::Element *F = script->func_ptrs_to_update_elems.front(); F; F = F->next()) { + bool is_dying_thread_entry = F->get().mutex == &func_ptrs_to_update_thread_local.mutex; + if (is_dying_thread_entry) { + // This may lead to multiple main-thread entries, but that's not a problem + // and allows to reuse the element, which is needed, since it's tracked by pointer. + F->get().element = new_E; + F->get().mutex = &func_ptrs_to_update_main_thread->mutex; + } + } + } + } + + E->erase(); + } + } + func_ptrs_to_update_main_thread->initialized = true; + + { + // Remove orphan thread-to-script entries from every script. + // FIXME: This involves iterating through every script whenever a thread dies. + // While it's OK that thread creation/destruction are heavy operations, + // additional bookkeeping can be used to outperform this brute-force approach. + + GDScriptLanguage *gd_lang = GDScriptLanguage::get_singleton(); + + MutexLock lock2(gd_lang->mutex); + + for (SelfList<GDScript> *s = gd_lang->script_list.first(); s; s = s->next()) { + GDScript *script = s->self(); + for (List<UpdatableFuncPtr *>::Element *E = script->func_ptrs_to_update.front(); E; E = E->next()) { + bool is_dying_thread_entry = &E->get()->mutex == &func_ptrs_to_update_thread_local.mutex; + if (is_dying_thread_entry) { + E->erase(); + break; + } + } + } + } +} + void GDScript::clear(ClearData *p_clear_data) { if (clearing) { return; @@ -1396,6 +1511,17 @@ void GDScript::clear(ClearData *p_clear_data) { is_root = true; } + { + MutexLock outer_lock(func_ptrs_to_update_mutex); + for (UpdatableFuncPtr *updatable : func_ptrs_to_update) { + MutexLock inner_lock(updatable->mutex); + for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { + *func_ptr_ptr = nullptr; + } + } + func_ptrs_to_update_elems.clear(); + } + RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); for (GDScript *E : must_clear_dependencies) { clear_data->scripts.insert(E); @@ -2013,6 +2139,10 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { named_globals.erase(p_name); } +void GDScriptLanguage::thread_exit() { + GDScript::_fixup_thread_function_bookkeeping(); +} + void GDScriptLanguage::init() { //populate global constants int gcc = CoreConstants::get_global_constant_count(); |