1
0
Fork 0

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.
This commit is contained in:
Pedro J. Estébanez 2024-11-08 15:01:52 +01:00
parent 1bffd6c73b
commit 56bdef9f6f
2 changed files with 66 additions and 45 deletions

View File

@ -750,6 +750,8 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
}
bool ClassDB::can_instantiate(const StringName &p_class) {
String script_path;
{
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@ -757,9 +759,8 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
if (!ScriptServer::is_global_class(p_class)) {
ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
}
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();
script_path = ScriptServer::get_global_class_path(p_class);
goto use_script; // Open the lock for resource loading.
}
#ifdef TOOLS_ENABLED
if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
@ -769,7 +770,14 @@ bool ClassDB::can_instantiate(const StringName &p_class) {
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) {
String script_path;
{
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@ -777,9 +785,8 @@ bool ClassDB::is_abstract(const StringName &p_class) {
if (!ScriptServer::is_global_class(p_class)) {
ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
}
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();
script_path = ScriptServer::get_global_class_path(p_class);
goto use_script; // Open the lock for resource loading.
}
if (ti->creation_func != nullptr) {
@ -795,7 +802,14 @@ bool ClassDB::is_abstract(const StringName &p_class) {
#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) {
String script_path;
{
OBJTYPE_RLOCK;
ClassInfo *ti = classes.getptr(p_class);
@ -803,9 +817,8 @@ bool ClassDB::is_virtual(const StringName &p_class) {
if (!ScriptServer::is_global_class(p_class)) {
ERR_FAIL_V_MSG(false, vformat("Cannot get class '%s'.", String(p_class)));
}
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();
script_path = ScriptServer::get_global_class_path(p_class);
goto use_script; // Open the lock for resource loading.
}
#ifdef TOOLS_ENABLED
if ((ti->api == API_EDITOR || ti->api == API_EDITOR_EXTENSION) && !Engine::get_singleton()->is_editor_hint()) {
@ -815,6 +828,11 @@ bool ClassDB::is_virtual(const StringName &p_class) {
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) {
OBJTYPE_WLOCK;

View File

@ -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);