diff options
Diffstat (limited to 'core/object')
-rw-r--r-- | core/object/class_db.cpp | 27 | ||||
-rw-r--r-- | core/object/object.cpp | 35 | ||||
-rw-r--r-- | core/object/object.h | 2 | ||||
-rw-r--r-- | core/object/script_language.cpp | 20 | ||||
-rw-r--r-- | core/object/script_language.h | 2 | ||||
-rw-r--r-- | core/object/script_language_extension.h | 36 | ||||
-rw-r--r-- | core/object/worker_thread_pool.cpp | 161 | ||||
-rw-r--r-- | core/object/worker_thread_pool.h | 24 |
8 files changed, 203 insertions, 104 deletions
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 80a2703c2f..7ef1ce74ed 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -182,8 +182,20 @@ public: // Construct a placeholder. Object *obj = native_parent->creation_func(); + + // ClassDB::set_object_extension_instance() won't be called for placeholders. + // We need need to make sure that all the things it would have done (even if + // done in a different way to support placeholders) will also be done here. + obj->_extension = ClassDB::get_placeholder_extension(ti->name); obj->_extension_instance = memnew(PlaceholderExtensionInstance(ti->name)); + +#ifdef TOOLS_ENABLED + if (obj->_extension->track_instance) { + obj->_extension->track_instance(obj->_extension->tracking_userdata, obj); + } +#endif + return obj; } @@ -506,14 +518,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require extension = get_placeholder_extension(ti->name); } #endif - Object *obj = (Object *)extension->create_instance(extension->class_userdata); - -#ifdef TOOLS_ENABLED - if (extension->track_instance) { - extension->track_instance(extension->tracking_userdata, obj); - } -#endif - return obj; + return (Object *)extension->create_instance(extension->class_userdata); } else { #ifdef TOOLS_ENABLED if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { @@ -638,6 +643,12 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & p_object->_extension = ti->gdextension; p_object->_extension_instance = p_instance; + +#ifdef TOOLS_ENABLED + if (p_object->_extension->track_instance) { + p_object->_extension->track_instance(p_object->_extension->tracking_userdata, p_object); + } +#endif } bool ClassDB::can_instantiate(const StringName &p_class) { diff --git a/core/object/object.cpp b/core/object/object.cpp index 8b6fd587e0..f8d2feb5a8 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -142,16 +142,16 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { args = p_dict["args"]; } - for (int i = 0; i < args.size(); i++) { - Dictionary d = args[i]; + for (const Variant &arg : args) { + Dictionary d = arg; mi.arguments.push_back(PropertyInfo::from_dict(d)); } Array defargs; if (p_dict.has("default_args")) { defargs = p_dict["default_args"]; } - for (int i = 0; i < defargs.size(); i++) { - mi.default_arguments.push_back(defargs[i]); + for (const Variant &defarg : defargs) { + mi.default_arguments.push_back(defarg); } if (p_dict.has("return")) { @@ -1100,6 +1100,20 @@ bool Object::_has_user_signal(const StringName &p_name) const { return signal_map[p_name].user.name.length() > 0; } +void Object::_remove_user_signal(const StringName &p_name) { + SignalData *s = signal_map.getptr(p_name); + ERR_FAIL_NULL_MSG(s, "Provided signal does not exist."); + ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal)."); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + Object *target = slot_kv.key.get_object(); + if (likely(target)) { + target->connections.erase(slot_kv.value.cE); + } + } + + signal_map.erase(p_name); +} + Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (unlikely(p_argcount < 1)) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1233,8 +1247,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { MethodInfo mi; mi.name = p_name; - for (int i = 0; i < p_args.size(); i++) { - Dictionary d = p_args[i]; + for (const Variant &arg : p_args) { + Dictionary d = arg; PropertyInfo param; if (d.has("name")) { @@ -1248,6 +1262,10 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { } add_user_signal(mi); + + if (signal_map.has(p_name)) { + signal_map.getptr(p_name)->removable = true; + } } TypedArray<Dictionary> Object::_get_signal_list() const { @@ -1585,8 +1603,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { } break; case Variant::ARRAY: { Array a = p_var; - for (int i = 0; i < a.size(); i++) { - _clear_internal_resource_paths(a[i]); + for (const Variant &var : a) { + _clear_internal_resource_paths(var); } } break; @@ -1661,6 +1679,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal); + ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::_remove_user_signal); { MethodInfo mi; diff --git a/core/object/object.h b/core/object/object.h index d9551ecd01..915c3a8c25 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -619,6 +619,7 @@ private: MethodInfo user; HashMap<Callable, Slot, HashableHasher<Callable>> slot_map; + bool removable = false; }; HashMap<StringName, SignalData> signal_map; @@ -646,6 +647,7 @@ private: void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; + void _remove_user_signal(const StringName &p_name); Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); TypedArray<Dictionary> _get_signal_list() const; TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const; diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 73da0ba2af..bd3199ca0a 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -51,7 +51,7 @@ void Script::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POSTINITIALIZE: { if (EngineDebugger::is_active()) { - EngineDebugger::get_script_debugger()->set_break_language(get_language()); + callable_mp(this, &Script::_set_debugger_break_language).call_deferred(); } } break; } @@ -103,6 +103,12 @@ Dictionary Script::_get_script_constant_map() { return ret; } +void Script::_set_debugger_break_language() { + if (EngineDebugger::is_active()) { + EngineDebugger::get_script_debugger()->set_break_language(get_language()); + } +} + int Script::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const { MethodInfo mi = get_method_info(p_method); @@ -251,8 +257,8 @@ void ScriptServer::init_languages() { if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { Array script_classes = GLOBAL_GET("_global_script_classes"); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary c = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary c = script_class; if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { continue; } @@ -263,8 +269,8 @@ void ScriptServer::init_languages() { #endif Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary c = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary c = script_class; if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { continue; } @@ -463,8 +469,8 @@ void ScriptServer::save_global_classes() { Dictionary class_icons; Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary d = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary d = script_class; if (!d.has("name") || !d.has("icon")) { continue; } diff --git a/core/object/script_language.h b/core/object/script_language.h index c6c6f3de9f..223f114150 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -124,6 +124,8 @@ protected: TypedArray<Dictionary> _get_script_signal_list(); Dictionary _get_script_constant_map(); + void _set_debugger_break_language(); + public: virtual void reload_from_file() override; diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 1db322526d..cc6b729ae8 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -319,8 +319,8 @@ public: } if (r_errors != nullptr && ret.has("errors")) { Array errors = ret["errors"]; - for (int i = 0; i < errors.size(); i++) { - Dictionary err = errors[i]; + for (const Variant &error : errors) { + Dictionary err = error; ERR_CONTINUE(!err.has("line")); ERR_CONTINUE(!err.has("column")); ERR_CONTINUE(!err.has("message")); @@ -339,8 +339,8 @@ public: if (r_warnings != nullptr && ret.has("warnings")) { ERR_FAIL_COND_V(!ret.has("warnings"), false); Array warnings = ret["warnings"]; - for (int i = 0; i < warnings.size(); i++) { - Dictionary warn = warnings[i]; + for (const Variant &warning : warnings) { + Dictionary warn = warning; ERR_CONTINUE(!warn.has("start_line")); ERR_CONTINUE(!warn.has("end_line")); ERR_CONTINUE(!warn.has("leftmost_column")); @@ -402,8 +402,8 @@ public: if (r_options != nullptr && ret.has("options")) { Array options = ret["options"]; - for (int i = 0; i < options.size(); i++) { - Dictionary op = options[i]; + for (const Variant &var : options) { + Dictionary op = var; CodeCompletionOption option; ERR_CONTINUE(!op.has("kind")); option.kind = CodeCompletionKind(int(op["kind"])); @@ -502,8 +502,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -522,8 +522,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -549,8 +549,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -562,9 +562,9 @@ public: TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret); Vector<StackInfo> sret; - for (int i = 0; i < ret.size(); i++) { + for (const Variant &var : ret) { StackInfo si; - Dictionary d = ret[i]; + Dictionary d = var; ERR_CONTINUE(!d.has("file")); ERR_CONTINUE(!d.has("func")); ERR_CONTINUE(!d.has("line")); @@ -595,8 +595,8 @@ public: virtual void get_public_functions(List<MethodInfo> *p_functions) const override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret); - for (int i = 0; i < ret.size(); i++) { - MethodInfo mi = MethodInfo::from_dict(ret[i]); + for (const Variant &var : ret) { + MethodInfo mi = MethodInfo::from_dict(var); p_functions->push_back(mi); } } @@ -615,8 +615,8 @@ public: virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret); - for (int i = 0; i < ret.size(); i++) { - MethodInfo mi = MethodInfo::from_dict(ret[i]); + for (const Variant &var : ret) { + MethodInfo mi = MethodInfo::from_dict(var); p_annotations->push_back(mi); } } diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index ef3d315e4b..9c9e0fa899 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -35,6 +35,8 @@ #include "core/os/thread_safe.h" #include "core/templates/command_queue_mt.h" +WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1; + void WorkerThreadPool::Task::free_template_userdata() { ERR_FAIL_NULL(template_userdata); ERR_FAIL_NULL(native_func_userdata); @@ -60,14 +62,19 @@ void WorkerThreadPool::_process_task(Task *p_task) { // its pre-created threads can't have ScriptServer::thread_enter() called on them early. // Therefore, we do it late at the first opportunity, so in case the task // about to be run uses scripting, guarantees are held. + task_mutex.lock(); if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) { + task_mutex.unlock(); ScriptServer::thread_enter(); + task_mutex.lock(); curr_thread.ready_for_scripting = true; } - task_mutex.lock(); p_task->pool_thread_index = pool_thread_index; prev_task = curr_thread.current_task; curr_thread.current_task = p_task; + if (p_task->pending_notify_yield_over) { + curr_thread.yield_is_over = true; + } task_mutex.unlock(); } #endif @@ -389,83 +396,119 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { task_mutex.unlock(); if (caller_pool_thread) { - while (true) { - Task *task_to_process = nullptr; - { - MutexLock lock(task_mutex); - bool was_signaled = caller_pool_thread->signaled; - caller_pool_thread->signaled = false; - - if (task->completed) { - // This thread was awaken also for some reason, but it's about to exit. - // Let's find out what may be pending and forward the requests. - if (!exit_threads && was_signaled) { - uint32_t to_process = task_queue.first() ? 1 : 0; - uint32_t to_promote = caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0; - if (to_process || to_promote) { - // This thread must be left alone since it won't loop again. - caller_pool_thread->signaled = true; - _notify_threads(caller_pool_thread, to_process, to_promote); - } - } + _wait_collaboratively(caller_pool_thread, task); + task->waiting_pool--; + if (task->waiting_pool == 0 && task->waiting_user == 0) { + tasks.erase(p_task_id); + task_allocator.free(task); + } + } else { + task->done_semaphore.wait(); + task_mutex.lock(); + task->waiting_user--; + if (task->waiting_pool == 0 && task->waiting_user == 0) { + tasks.erase(p_task_id); + task_allocator.free(task); + } + task_mutex.unlock(); + } - task->waiting_pool--; - if (task->waiting_pool == 0 && task->waiting_user == 0) { - tasks.erase(p_task_id); - task_allocator.free(task); - } + return OK; +} - break; - } +void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) { + // Keep processing tasks until the condition to stop waiting is met. - if (!exit_threads) { - // This is a thread from the pool. It shouldn't just idle. - // Let's try to process other tasks while we wait. +#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed) - if (caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) { - if (_try_promote_low_priority_task()) { - _notify_threads(caller_pool_thread, 1, 0); - } + while (true) { + Task *task_to_process = nullptr; + { + MutexLock lock(task_mutex); + bool was_signaled = p_caller_pool_thread->signaled; + p_caller_pool_thread->signaled = false; + + if (IS_WAIT_OVER) { + p_caller_pool_thread->yield_is_over = false; + if (!exit_threads && was_signaled) { + // This thread was awaken for some additional reason, but it's about to exit. + // Let's find out what may be pending and forward the requests. + uint32_t to_process = task_queue.first() ? 1 : 0; + uint32_t to_promote = p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0; + if (to_process || to_promote) { + // This thread must be left alone since it won't loop again. + p_caller_pool_thread->signaled = true; + _notify_threads(p_caller_pool_thread, to_process, to_promote); } + } - if (singleton->task_queue.first()) { - task_to_process = task_queue.first()->self(); - task_queue.remove(task_queue.first()); + break; + } + + if (!exit_threads) { + if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) { + if (_try_promote_low_priority_task()) { + _notify_threads(p_caller_pool_thread, 1, 0); } + } - if (!task_to_process) { - caller_pool_thread->awaited_task = task; + if (singleton->task_queue.first()) { + task_to_process = task_queue.first()->self(); + task_queue.remove(task_queue.first()); + } - if (flushing_cmd_queue) { - flushing_cmd_queue->unlock(); - } - caller_pool_thread->cond_var.wait(lock); - if (flushing_cmd_queue) { - flushing_cmd_queue->lock(); - } + if (!task_to_process) { + p_caller_pool_thread->awaited_task = p_task; - DEV_ASSERT(exit_threads || caller_pool_thread->signaled || task->completed); - caller_pool_thread->awaited_task = nullptr; + if (flushing_cmd_queue) { + flushing_cmd_queue->unlock(); + } + p_caller_pool_thread->cond_var.wait(lock); + if (flushing_cmd_queue) { + flushing_cmd_queue->lock(); } + + DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER); + p_caller_pool_thread->awaited_task = nullptr; } } + } - if (task_to_process) { - _process_task(task_to_process); - } + if (task_to_process) { + _process_task(task_to_process); } - } else { - task->done_semaphore.wait(); - task_mutex.lock(); - task->waiting_user--; - if (task->waiting_pool == 0 && task->waiting_user == 0) { - tasks.erase(p_task_id); - task_allocator.free(task); + } +} + +void WorkerThreadPool::yield() { + int th_index = get_thread_index(); + ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread."); + _wait_collaboratively(&threads[th_index], ThreadData::YIELDING); +} + +void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { + task_mutex.lock(); + Task **taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_MSG("Invalid Task ID."); + } + Task *task = *taskp; + if (task->pool_thread_index == -1) { // Completed or not started yet. + if (!task->completed) { + // This avoids a race condition where a task is created and yield-over called before it's processed. + task->pending_notify_yield_over = true; } task_mutex.unlock(); + return; } - return OK; + ThreadData &td = threads[task->pool_thread_index]; + td.yield_is_over = true; + td.signaled = true; + td.cond_var.notify_one(); + + task_mutex.unlock(); } WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h index fdddc9a647..a9cf260a0f 100644 --- a/core/object/worker_thread_pool.h +++ b/core/object/worker_thread_pool.h @@ -81,7 +81,8 @@ private: void *native_func_userdata = nullptr; String description; Semaphore done_semaphore; // For user threads awaiting. - bool completed = false; + bool completed : 1; + bool pending_notify_yield_over : 1; Group *group = nullptr; SelfList<Task> task_elem; uint32_t waiting_pool = 0; @@ -92,6 +93,8 @@ private: void free_template_userdata(); Task() : + completed(false), + pending_notify_yield_over(false), task_elem(this) {} }; @@ -107,13 +110,21 @@ private: BinaryMutex task_mutex; struct ThreadData { + static Task *const YIELDING; // Too bad constexpr doesn't work here. + uint32_t index = 0; Thread thread; - bool ready_for_scripting = false; - bool signaled = false; + bool ready_for_scripting : 1; + bool signaled : 1; + bool yield_is_over : 1; Task *current_task = nullptr; - Task *awaited_task = nullptr; // Null if not awaiting the condition variable. Special value for idle-waiting. + Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING). ConditionVariable cond_var; + + ThreadData() : + ready_for_scripting(false), + signaled(false), + yield_is_over(false) {} }; TightLocalVector<ThreadData> threads; @@ -177,6 +188,8 @@ private: } }; + void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task); + protected: static void _bind_methods(); @@ -196,6 +209,9 @@ public: bool is_task_completed(TaskID p_task_id) const; Error wait_for_task_completion(TaskID p_task_id); + void yield(); + void notify_yield_over(TaskID p_task_id); + template <typename C, typename M, typename U> GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) { typedef GroupUserData<C, M, U> GroupUD; |