From d4175f967670230b558d5a2a2692c0820e3f3463 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 12 Jun 2023 21:13:26 +0200 Subject: [PATCH] Ported parts of : Refactor Node Processing * Node processing works on the concept of process groups. * A node group can be inherited, run on main thread, or a sub-thread. * Groups can be ordered. * Process priority is now present for physics. This is the first steps towards implementing godotengine/godot-proposals#6424. No threading or thread guards exist yet in most of the scene code other than Node. That will have to be added later. - reduz https://github.com/godotengine/godot/commit/98c655ec8db17e50afa58284b1dcad754034db4b - Only got the smaller improvements, and the thread safety for Node and SceneTree. I'm planning to implement a similar system, but I have a different way of doing it in mind. --- core/containers/hash_map.h | 4 +- core/containers/hash_set.h | 4 +- core/containers/local_vector.h | 10 +- core/containers/vector.h | 6 +- core/object/object.cpp | 9 +- core/object/object.h | 8 +- core/os/thread.h | 4 + doc/classes/Object.xml | 6 + scene/main/node.cpp | 15 ++ scene/main/node.h | 4 + scene/main/scene_tree.cpp | 293 ++++++++++++++++++++++----------- 11 files changed, 253 insertions(+), 110 deletions(-) diff --git a/core/containers/hash_map.h b/core/containers/hash_map.h index 692991e76d..afcc353f6b 100644 --- a/core/containers/hash_map.h +++ b/core/containers/hash_map.h @@ -99,7 +99,7 @@ class HashMap { } void clear() { - if (elements == nullptr) { + if (elements == nullptr || num_elements == 0) { return; } uint32_t capacity = hash_table_size_primes[capacity_index]; @@ -517,7 +517,7 @@ class HashMap { } bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { - if (elements == nullptr) { + if (elements == nullptr || num_elements == 0) { return false; // Failed lookups, no elements } diff --git a/core/containers/hash_set.h b/core/containers/hash_set.h index e6cceb3b9d..f90a3bad86 100644 --- a/core/containers/hash_set.h +++ b/core/containers/hash_set.h @@ -79,7 +79,7 @@ class HashSet { } bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) const { - if (keys == nullptr) { + if (keys == nullptr || num_elements == 0) { return false; // Failed lookups, no elements } @@ -236,7 +236,7 @@ class HashSet { } void clear() { - if (keys == nullptr) { + if (keys == nullptr || num_elements == 0) { return; } uint32_t capacity = hash_table_size_primes[capacity_index]; diff --git a/core/containers/local_vector.h b/core/containers/local_vector.h index 88b6386e88..ad8a56d0f3 100644 --- a/core/containers/local_vector.h +++ b/core/containers/local_vector.h @@ -30,11 +30,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/error/error_macros.h" -#include "core/os/memory.h" #include "core/containers/pool_vector.h" #include "core/containers/sort_array.h" #include "core/containers/vector.h" +#include "core/error/error_macros.h" +#include "core/os/memory.h" template class LocalVector { @@ -94,11 +94,13 @@ class LocalVector { } } - void erase(const T &p_val) { + _FORCE_INLINE_ bool erase(const T &p_val) { int64_t idx = find(p_val); if (idx >= 0) { remove(idx); + return true; } + return false; } U erase_multiple_unordered(const T &p_val) { @@ -280,7 +282,7 @@ class LocalVector { data[i] = r[i]; } } - + inline LocalVector &operator=(const LocalVector &p_from) { resize(p_from.size()); for (U i = 0; i < p_from.count; i++) { diff --git a/core/containers/vector.h b/core/containers/vector.h index 01b325d808..1aebcc0345 100644 --- a/core/containers/vector.h +++ b/core/containers/vector.h @@ -37,9 +37,9 @@ */ #include "core/containers/cowdata.h" +#include "core/containers/sort_array.h" #include "core/error/error_macros.h" #include "core/os/memory.h" -#include "core/containers/sort_array.h" template class VectorWriteProxy { @@ -65,11 +65,13 @@ class Vector { bool push_back(T p_elem); void remove(int p_index) { _cowdata.remove(p_index); } - void erase(const T &p_val) { + _FORCE_INLINE_ bool erase(const T &p_val) { int idx = find(p_val); if (idx >= 0) { remove(idx); + return true; } + return false; }; void invert(); diff --git a/core/object/object.cpp b/core/object/object.cpp index 3de749b704..e33ebf2d39 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -391,6 +391,10 @@ bool Object::_predelete() { return _predelete_ok; } +void Object::cancel_free() { + _predelete_ok = 0; +} + void Object::_postinitialize() { _class_ptr = _get_class_namev(); _initialize_classv(); @@ -957,10 +961,6 @@ void Object::property_list_changed_notify() { _change_notify(); } -void Object::cancel_delete() { - _predelete_ok = true; -} - ObjectRC *Object::_use_rc() { // The RC object is lazily created the first time it's requested; // that way, there's no need to allocate and release it at all if this Object @@ -1734,6 +1734,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("tr", "message"), &Object::tr); ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion); + ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free); ClassDB::add_virtual_method("Object", MethodInfo("free"), false); diff --git a/core/object/object.h b/core/object/object.h index 24ccfb7669..a0e38690d1 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -33,12 +33,12 @@ #include "core/containers/hash_map.h" #include "core/containers/list.h" #include "core/containers/rb_map.h" +#include "core/containers/rb_set.h" +#include "core/containers/vmap.h" #include "core/object/object_id.h" #include "core/os/rw_lock.h" #include "core/os/safe_refcount.h" -#include "core/containers/rb_set.h" #include "core/variant/variant.h" -#include "core/containers/vmap.h" #include @@ -565,8 +565,6 @@ class Object { static void get_valid_parents_static(List *p_parents); static void _get_valid_parents_static(List *p_parents); - void cancel_delete(); - void property_list_changed_notify(); virtual void _changed_callback(Object *p_changed, const char *p_prop); @@ -809,6 +807,8 @@ class Object { void clear_internal_resource_paths(); + void cancel_free(); + Object(); virtual ~Object(); }; diff --git a/core/os/thread.h b/core/os/thread.h index b9e4e42a65..21acad4334 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -96,6 +96,8 @@ class Thread { // get the ID of the main thread _FORCE_INLINE_ static ID get_main_id() { return main_thread_id; } + _FORCE_INLINE_ static bool is_main_thread() { return get_caller_id() == main_thread_id; } + static Error set_name(const String &p_name); void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()); @@ -110,6 +112,8 @@ class Thread { _FORCE_INLINE_ static ID get_caller_id() { return 0; } // get the ID of the main thread _FORCE_INLINE_ static ID get_main_id() { return 0; } + + _FORCE_INLINE_ static bool is_main_thread() { return true; } static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index a4229102b2..4c00eb56f2 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -100,6 +100,12 @@ Returns [code]true[/code] if the object can translate strings. See [method set_message_translation] and [method tr]. + + + + If this method is called during [constant NOTIFICATION_PREDELETE], this object will reject being freed and will remain allocated. This is mostly an internal function used for error handling to avoid the user from freeing objects when they are not intended to. + + diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 249b1e2358..6231b6b5c4 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -179,6 +179,12 @@ void Node::_notification(int p_notification) { data.in_constructor = false; } break; case NOTIFICATION_PREDELETE: { + if (data.inside_tree && !Thread::is_main_thread()) { + cancel_free(); + ERR_PRINT("Attempted to free a node that is currently added to the SceneTree from a thread. This is not permitted, use queue_free() instead. Node has not been freed."); + return; + } + if (data.parent) { data.parent->remove_child(this); } @@ -420,6 +426,7 @@ void Node::_propagate_exit_tree() { } void Node::move_child(Node *p_child, int p_pos) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Moving child node positions inside the SceneTree is only allowed from the main thread. Use call_deferred(\"move_child\",child,index)."); ERR_FAIL_NULL(p_child); ERR_FAIL_INDEX_MSG(static_cast(p_pos), data.children.size() + 1, vformat("Invalid new child position: %d.", p_pos)); ERR_FAIL_COND_MSG(p_child->data.parent != this, "Child is not a child of this node."); @@ -1270,6 +1277,8 @@ void Node::_set_name_nocheck(const StringName &p_name) { } void Node::set_name(const String &p_name) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Changing the name to nodes inside the SceneTree is only allowed from the main thread. Use call_deferred(\"set_name\",new_name)."); + String name = p_name.validate_node_name(); ERR_FAIL_COND(name == ""); @@ -1504,6 +1513,7 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name) { } void Node::add_child(Node *p_child, bool p_force_readable_name) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Adding children to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_child\",node)."); ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent @@ -1532,6 +1542,7 @@ void Node::add_child_below_node(Node *p_node, Node *p_child, bool p_force_readab } void Node::remove_child(Node *p_child) { + ERR_FAIL_COND_MSG(data.inside_tree && !Thread::is_main_thread(), "Removing children from a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"remove_child\",node)."); ERR_FAIL_NULL(p_child); ERR_FAIL_COND_MSG(data.blocked > 0, "Parent node is busy setting up children, remove_node() failed. Consider using call_deferred(\"remove_child\", child) instead."); @@ -1881,6 +1892,8 @@ void Node::_acquire_unique_name_in_owner() { } void Node::set_unique_name_in_owner(bool p_enabled) { + ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead."); + if (data.unique_name_in_owner == p_enabled) { return; } @@ -1902,6 +1915,8 @@ bool Node::is_unique_name_in_owner() const { } void Node::set_owner(Node *p_owner) { + ERR_FAIL_COND_MSG(is_inside_tree() && !Thread::is_main_thread(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead."); + if (data.owner) { if (data.unique_name_in_owner) { _release_unique_name_in_owner(); diff --git a/scene/main/node.h b/scene/main/node.h index 589bd2de4b..5b06c1c396 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -467,6 +467,10 @@ class Node : public Object { void set_process_unhandled_key_input(bool p_enable); bool is_processing_unhandled_key_input() const; + _FORCE_INLINE_ bool _is_any_processing() const { + return data.idle_process || data.idle_process_internal || data.physics_process || data.physics_process_internal; + } + int get_position_in_parent() const; Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const; diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index da05414c1c..37c432751a 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -143,6 +143,8 @@ void SceneTree::node_added(Node *p_node) { } void SceneTree::node_removed(Node *p_node) { + // Nodes can only be removed from the main thread. + if (current_scene == p_node) { current_scene = nullptr; } @@ -157,6 +159,8 @@ void SceneTree::node_renamed(Node *p_node) { } SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_node) { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); if (!E) { E = group_map.insert(p_group, Group()); @@ -170,6 +174,8 @@ SceneTree::Group *SceneTree::add_to_group(const StringName &p_group, Node *p_nod } void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); ERR_FAIL_COND(!E); @@ -180,6 +186,8 @@ void SceneTree::remove_from_group(const StringName &p_group, Node *p_node) { } void SceneTree::make_group_changed(const StringName &p_group) { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); if (E) { E->get().changed = true; @@ -187,6 +195,8 @@ void SceneTree::make_group_changed(const StringName &p_group) { } void SceneTree::flush_transform_notifications() { + _THREAD_SAFE_METHOD_ + SelfList *n = xform_change_list.first(); while (n) { Node *node = n->self(); @@ -239,47 +249,56 @@ void SceneTree::_update_group_order(Group &g, bool p_use_priority) { } void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) { - RBMap::Element *E = group_map.find(p_group); - if (!E) { - return; - } - Group &g = E->get(); - if (g.nodes.empty()) { - return; - } - - if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) { - ERR_FAIL_COND(ugc_locked); + Vector nodes_copy; - UGCall ug; - ug.call = p_function; - ug.group = p_group; + { + _THREAD_SAFE_METHOD_ - if (unique_group_calls.has(ug)) { + RBMap::Element *E = group_map.find(p_group); + if (!E) { + return; + } + Group &g = E->get(); + if (g.nodes.empty()) { return; } - VARIANT_ARGPTRS; + if (p_call_flags & GROUP_CALL_UNIQUE && !(p_call_flags & GROUP_CALL_REALTIME)) { + ERR_FAIL_COND(ugc_locked); - Vector args; - for (int i = 0; i < VARIANT_ARG_MAX; i++) { - if (argptr[i]->get_type() == Variant::NIL) { - break; + UGCall ug; + ug.call = p_function; + ug.group = p_group; + + if (unique_group_calls.has(ug)) { + return; } - args.push_back(*argptr[i]); + + VARIANT_ARGPTRS; + + Vector args; + for (int i = 0; i < VARIANT_ARG_MAX; i++) { + if (argptr[i]->get_type() == Variant::NIL) { + break; + } + args.push_back(*argptr[i]); + } + + unique_group_calls[ug] = args; + return; } - unique_group_calls[ug] = args; - return; - } + _update_group_order(g); - _update_group_order(g); + nodes_copy = g.nodes; + } - Vector nodes_copy = g.nodes; Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); + _THREAD_SAFE_LOCK_ call_lock++; + _THREAD_SAFE_UNLOCK_ if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = node_count - 1; i >= 0; i--) { @@ -316,29 +335,39 @@ void SceneTree::call_group_flags(uint32_t p_call_flags, const StringName &p_grou } } + _THREAD_SAFE_LOCK_ call_lock--; if (call_lock == 0) { call_skip.clear(); } + _THREAD_SAFE_UNLOCK_ } void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification) { - RBMap::Element *E = group_map.find(p_group); - if (!E) { - return; - } - Group &g = E->get(); - if (g.nodes.empty()) { - return; - } + Vector nodes_copy; - _update_group_order(g); + { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); + if (!E) { + return; + } + Group &g = E->get(); + if (g.nodes.empty()) { + return; + } + + _update_group_order(g); + + nodes_copy = g.nodes; + } - Vector nodes_copy = g.nodes; Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); + _THREAD_SAFE_LOCK_ call_lock++; + _THREAD_SAFE_UNLOCK_ if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = node_count - 1; i >= 0; i--) { @@ -367,29 +396,40 @@ void SceneTree::notify_group_flags(uint32_t p_call_flags, const StringName &p_gr } } + _THREAD_SAFE_LOCK_ call_lock--; if (call_lock == 0) { call_skip.clear(); } + _THREAD_SAFE_UNLOCK_ } void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value) { - RBMap::Element *E = group_map.find(p_group); - if (!E) { - return; - } - Group &g = E->get(); - if (g.nodes.empty()) { - return; - } + Vector nodes_copy; - _update_group_order(g); + { + _THREAD_SAFE_METHOD_ + + RBMap::Element *E = group_map.find(p_group); + if (!E) { + return; + } + Group &g = E->get(); + if (g.nodes.empty()) { + return; + } + + _update_group_order(g); + + nodes_copy = g.nodes; + } - Vector nodes_copy = g.nodes; Node **nodes = nodes_copy.ptrw(); int node_count = nodes_copy.size(); + _THREAD_SAFE_LOCK_ call_lock++; + _THREAD_SAFE_UNLOCK_ if (p_call_flags & GROUP_CALL_REVERSE) { for (int i = node_count - 1; i >= 0; i--) { @@ -418,10 +458,12 @@ void SceneTree::set_group_flags(uint32_t p_call_flags, const StringName &p_group } } + _THREAD_SAFE_LOCK_ call_lock--; if (call_lock == 0) { call_skip.clear(); } + _THREAD_SAFE_UNLOCK_ } void SceneTree::call_group(const StringName &p_group, const StringName &p_function, VARIANT_ARG_DECLARE) { @@ -531,6 +573,8 @@ void SceneTree::init() { } void SceneTree::set_physics_interpolation_enabled(bool p_enabled) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "set_physics_interpolation_enabled can only be set from the main thread."); + // disallow interpolation in editor if (Engine::get_singleton()->is_editor_hint()) { p_enabled = false; @@ -669,35 +713,38 @@ bool SceneTree::idle(float p_time) { _flush_delete_queue(); //go through timers + { + _THREAD_SAFE_METHOD_ + + List>::Element *L = timers.back(); //last element + + for (List>::Element *E = timers.front(); E;) { + List>::Element *N = E->next(); + if (pause && !E->get()->is_pause_mode_process()) { + if (E == L) { + break; //break on last, so if new timers were added during list traversal, ignore them. + } + E = N; + continue; + } - List>::Element *L = timers.back(); //last element + float time_left = E->get()->get_time_left(); + if (E->get()->is_ignore_time_scale()) { + time_left -= Engine::get_singleton()->get_idle_frame_step(); + } else { + time_left -= p_time; + } + E->get()->set_time_left(time_left); - for (List>::Element *E = timers.front(); E;) { - List>::Element *N = E->next(); - if (pause && !E->get()->is_pause_mode_process()) { + if (time_left < 0) { + E->get()->emit_signal("timeout"); + timers.erase(E); + } if (E == L) { break; //break on last, so if new timers were added during list traversal, ignore them. } E = N; - continue; - } - - float time_left = E->get()->get_time_left(); - if (E->get()->is_ignore_time_scale()) { - time_left -= Engine::get_singleton()->get_idle_frame_step(); - } else { - time_left -= p_time; - } - E->get()->set_time_left(time_left); - - if (time_left < 0) { - E->get()->emit_signal("timeout"); - timers.erase(E); - } - if (E == L) { - break; //break on last, so if new timers were added during list traversal, ignore them. } - E = N; } process_tweens(p_time, false); @@ -746,6 +793,8 @@ bool SceneTree::idle(float p_time) { } void SceneTree::process_tweens(float p_delta, bool p_physics) { + _THREAD_SAFE_METHOD_ + // This methods works similarly to how SceneTreeTimers are handled. List>::Element *L = tweens.back(); @@ -805,6 +854,8 @@ void SceneTree::finish() { } void SceneTree::quit(int p_exit_code) { + _THREAD_SAFE_METHOD_ + if (p_exit_code >= 0) { // Override the exit code if a positive argument is given (the default is `-1`). // This is a shorthand for calling `set_exit_code()` on the OS singleton then quitting. @@ -956,6 +1007,8 @@ Color SceneTree::get_debug_collision_contact_color() const { } Ref SceneTree::get_debug_collision_material() { + _THREAD_SAFE_METHOD_ + if (collision_material.is_valid()) { return collision_material; } @@ -973,6 +1026,8 @@ Ref SceneTree::get_debug_collision_material() { } Ref SceneTree::get_debug_contact_mesh() { + _THREAD_SAFE_METHOD_ + if (debug_contact_mesh.is_valid()) { return debug_contact_mesh; } @@ -1030,6 +1085,8 @@ Ref SceneTree::get_debug_contact_mesh() { } void SceneTree::set_pause(bool p_enabled) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Pause can only be set from the main thread."); + if (p_enabled == pause) { return; } @@ -1047,20 +1104,26 @@ bool SceneTree::is_paused() const { } void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p_method, const Ref &p_input) { - RBMap::Element *E = group_map.find(p_group); - if (!E) { - return; - } - Group &g = E->get(); - if (g.nodes.empty()) { - return; - } + Vector nodes_copy; - _update_group_order(g); + { + _THREAD_SAFE_METHOD_ - //copy, so copy on write happens in case something is removed from process while being called - //performance is not lost because only if something is added/removed the vector is copied. - Vector nodes_copy = g.nodes; + RBMap::Element *E = group_map.find(p_group); + if (!E) { + return; + } + Group &g = E->get(); + if (g.nodes.empty()) { + return; + } + + _update_group_order(g); + + //copy, so copy on write happens in case something is removed from process while being called + //performance is not lost because only if something is added/removed the vector is copied. + nodes_copy = g.nodes; + } int node_count = nodes_copy.size(); Node **nodes = nodes_copy.ptrw(); @@ -1068,7 +1131,9 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p Variant arg = p_input; const Variant *v[1] = { &arg }; + _THREAD_SAFE_LOCK_ call_lock++; + _THREAD_SAFE_UNLOCK_ for (int i = node_count - 1; i >= 0; i--) { if (input_handled) { @@ -1088,32 +1153,41 @@ void SceneTree::_call_input_pause(const StringName &p_group, const StringName &p //ERR_FAIL_COND(node_count != g.nodes.size()); } + _THREAD_SAFE_LOCK_ call_lock--; if (call_lock == 0) { call_skip.clear(); } + _THREAD_SAFE_UNLOCK_ } void SceneTree::_notify_group_pause(const StringName &p_group, int p_notification) { - RBMap::Element *E = group_map.find(p_group); - if (!E) { - return; - } - Group &g = E->get(); - if (g.nodes.empty()) { - return; - } + Vector nodes_copy; - _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); + { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); + if (!E) { + return; + } + Group &g = E->get(); + if (g.nodes.empty()) { + return; + } + + _update_group_order(g, p_notification == Node::NOTIFICATION_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PROCESS || p_notification == Node::NOTIFICATION_PHYSICS_PROCESS || p_notification == Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - //copy, so copy on write happens in case something is removed from process while being called - //performance is not lost because only if something is added/removed the vector is copied. - Vector nodes_copy = g.nodes; + //copy, so copy on write happens in case something is removed from process while being called + //performance is not lost because only if something is added/removed the vector is copied. + nodes_copy = g.nodes; + } int node_count = nodes_copy.size(); Node **nodes = nodes_copy.ptrw(); + _THREAD_SAFE_LOCK_ call_lock++; + _THREAD_SAFE_UNLOCK_ for (int i = 0; i < node_count; i++) { Node *n = nodes[i]; @@ -1132,10 +1206,12 @@ void SceneTree::_notify_group_pause(const StringName &p_group, int p_notificatio //ERR_FAIL_COND(node_count != g.nodes.size()); } + _THREAD_SAFE_LOCK_ call_lock--; if (call_lock == 0) { call_skip.clear(); } + _THREAD_SAFE_UNLOCK_ } /* @@ -1198,6 +1274,8 @@ int64_t SceneTree::get_event_count() const { } Array SceneTree::_get_nodes_in_group(const StringName &p_group) { + _THREAD_SAFE_METHOD_ + Array ret; RBMap::Element *E = group_map.find(p_group); if (!E) { @@ -1221,9 +1299,13 @@ Array SceneTree::_get_nodes_in_group(const StringName &p_group) { } bool SceneTree::has_group(const StringName &p_identifier) const { + _THREAD_SAFE_METHOD_ + return group_map.has(p_identifier); } void SceneTree::get_nodes_in_group(const StringName &p_group, List *p_list) { + _THREAD_SAFE_METHOD_ + RBMap::Element *E = group_map.find(p_group); if (!E) { return; @@ -1442,6 +1524,7 @@ Node *SceneTree::get_edited_scene_root() const { } void SceneTree::set_current_scene(Node *p_scene) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread."); ERR_FAIL_COND(p_scene && p_scene->get_parent() != root); current_scene = p_scene; } @@ -1451,6 +1534,8 @@ Node *SceneTree::get_current_scene() const { } void SceneTree::_change_scene(Node *p_to) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Changing scene can only be done from the main thread."); + if (current_scene) { memdelete(current_scene); current_scene = nullptr; @@ -1471,6 +1556,8 @@ void SceneTree::_change_scene(Node *p_to) { } Error SceneTree::change_scene(const String &p_path) { + ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Changing scene can only be done from the main thread."); + Ref new_scene = ResourceLoader::load(p_path); if (new_scene.is_null()) { return ERR_CANT_OPEN; @@ -1491,12 +1578,14 @@ Error SceneTree::change_scene_to(const Ref &p_scene) { } Error SceneTree::reload_current_scene() { + ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), ERR_INVALID_PARAMETER, "Reloading scene can only be done from the main thread."); ERR_FAIL_COND_V(!current_scene, ERR_UNCONFIGURED); String fname = current_scene->get_filename(); return change_scene(fname); } void SceneTree::add_current_scene(Node *p_current) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Adding a current scene can only be done from the main thread."); current_scene = p_current; root->add_child(p_current); } @@ -1910,6 +1999,8 @@ void SceneTree::global_menu_action(const Variant &p_id, const Variant &p_meta) { } Ref SceneTree::create_timer(float p_delay_sec, bool p_process_pause) { + _THREAD_SAFE_METHOD_ + Ref stt; stt.instance(); stt->set_pause_mode_process(p_process_pause); @@ -1919,12 +2010,16 @@ Ref SceneTree::create_timer(float p_delay_sec, bool p_process_pa } Ref SceneTree::create_tween() { + _THREAD_SAFE_METHOD_ + Ref tween = memnew(SceneTreeTween(true)); tweens.push_back(tween); return tween; } Array SceneTree::get_processed_tweens() { + _THREAD_SAFE_METHOD_ + Array ret; ret.resize(tweens.size()); @@ -1956,10 +2051,14 @@ void SceneTree::_server_disconnected() { } Ref SceneTree::get_multiplayer() const { + ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref(), "Multiplayer can only be manipulated from the main thread."); + return multiplayer; } void SceneTree::set_multiplayer_poll_enabled(bool p_enabled) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread."); + multiplayer_poll = p_enabled; } @@ -1968,6 +2067,8 @@ bool SceneTree::is_multiplayer_poll_enabled() const { } void SceneTree::set_multiplayer(Ref p_multiplayer) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread."); + ERR_FAIL_COND(!p_multiplayer.is_valid()); if (multiplayer.is_valid()) { @@ -1989,10 +2090,14 @@ void SceneTree::set_multiplayer(Ref p_multiplayer) { } void SceneTree::set_network_peer(const Ref &p_network_peer) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread."); + multiplayer->set_network_peer(p_network_peer); } Ref SceneTree::get_network_peer() const { + ERR_FAIL_COND_V_MSG(!Thread::is_main_thread(), Ref(), "Multiplayer can only be manipulated from the main thread."); + return multiplayer->get_network_peer(); } @@ -2017,6 +2122,8 @@ int SceneTree::get_rpc_sender_id() const { } void SceneTree::set_refuse_new_network_connections(bool p_refuse) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Multiplayer can only be manipulated from the main thread."); + multiplayer->set_refuse_new_network_connections(p_refuse); } @@ -2187,6 +2294,8 @@ void SceneTree::add_idle_callback(IdleCallback p_callback) { } void SceneTree::set_use_font_oversampling(bool p_oversampling) { + ERR_FAIL_COND_MSG(!Thread::is_main_thread(), "Font Oversampling can only be set from the main thread."); + if (use_font_oversampling == p_oversampling) { return; }