summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPedro J. Estébanez <pedrojrulez@gmail.com>2024-11-08 15:01:52 +0100
committerPedro J. Estébanez <pedrojrulez@gmail.com>2024-11-08 18:15:58 +0100
commit56bdef9f6f8cceaf4b2331ef155d177571dc6e96 (patch)
tree01737392ee3783b4698fb41f74c08043455dcb4c
parent1bffd6c73b44b85e5889f54e14b2193940cf5bb1 (diff)
downloadredot-engine-56bdef9f6f8cceaf4b2331ef155d177571dc6e96.tar.gz
Fix deadlocks related to ClassDB queries about global classes
`ClassDB::can_instantiate()` and other reflection methods deadlock if the type is an script global class, when such script indirectly uses a not-yet-registered class. The reason is the `ClassDB` read lock is still held when invoking the `ResourceLoader` to load the class script, which may in turn need to lock for writing (for the class registration). In particular, this happens with some types related to animation tree, that aren't registered at engine startup, but can happen with others, especially ones from the user. Registration statements are also added for the animation-related types that were lacking them.
-rw-r--r--core/object/class_db.cpp104
-rw-r--r--scene/register_scene_types.cpp3
2 files changed, 64 insertions, 43 deletions
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index bad224eff4..d48e1a3622 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -750,69 +750,87 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
}
bool ClassDB::can_instantiate(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
- }
#ifdef TOOLS_ENABLED
- if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
- return false;
- }
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
#endif
- return _can_instantiate(ti);
+ return _can_instantiate(ti);
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && !scr->is_abstract();
}
bool ClassDB::is_abstract(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && scr->is_abstract();
- }
- if (ti->creation_func != nullptr) {
- return false;
- }
- if (!ti->gdextension) {
- return true;
- }
+ if (ti->creation_func != nullptr) {
+ return false;
+ }
+ if (!ti->gdextension) {
+ return true;
+ }
#ifndef DISABLE_DEPRECATED
- return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
+ return ti->gdextension->create_instance2 == nullptr && ti->gdextension->create_instance == nullptr;
#else
- return ti->gdextension->create_instance2 == nullptr;
+ return ti->gdextension->create_instance2 == nullptr;
#endif // DISABLE_DEPRECATED
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
bool ClassDB::is_virtual(const StringName &p_class) {
- OBJTYPE_RLOCK;
+ String script_path;
+ {
+ OBJTYPE_RLOCK;
- ClassInfo *ti = classes.getptr(p_class);
- if (!ti) {
- if (!ScriptServer::is_global_class(p_class)) {
- ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ ClassInfo *ti = classes.getptr(p_class);
+ if (!ti) {
+ if (!ScriptServer::is_global_class(p_class)) {
+ ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
+ }
+ script_path = ScriptServer::get_global_class_path(p_class);
+ goto use_script; // Open the lock for resource loading.
}
- String path = ScriptServer::get_global_class_path(p_class);
- Ref<Script> scr = ResourceLoader::load(path);
- return scr.is_valid() && scr->is_valid() && scr->is_abstract();
- }
#ifdef TOOLS_ENABLED
- if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
- return false;
- }
+ if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
+ return false;
+ }
#endif
- return (_can_instantiate(ti) && ti->is_virtual);
+ return (_can_instantiate(ti) && ti->is_virtual);
+ }
+
+use_script:
+ Ref<Script> scr = ResourceLoader::load(script_path);
+ return scr.is_valid() && scr->is_valid() && scr->is_abstract();
}
void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 6b1ce2b4ca..315ccaeb8c 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -512,6 +512,9 @@ void register_scene_types() {
GDREGISTER_CLASS(AnimationNodeStateMachine);
GDREGISTER_CLASS(AnimationNodeStateMachinePlayback);
+ GDREGISTER_INTERNAL_CLASS(AnimationNodeStartState);
+ GDREGISTER_INTERNAL_CLASS(AnimationNodeEndState);
+
GDREGISTER_CLASS(AnimationNodeSync);
GDREGISTER_CLASS(AnimationNodeStateMachineTransition);
GDREGISTER_CLASS(AnimationNodeOutput);