diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index e9f812ab1c7..ac1870fe88f 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -302,6 +302,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (!Thread::is_main_thread()) { mq_override = memnew(CallQueue); MessageQueue::set_thread_singleton_override(mq_override); + set_current_thread_safe_for_nodes(true); } } else { DEV_ASSERT(load_task.dependent_path.is_empty()); @@ -357,6 +358,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (load_nesting == 0 && mq_override) { memdelete(mq_override); + set_current_thread_safe_for_nodes(false); } } diff --git a/core/os/thread_safe.cpp b/core/os/thread_safe.cpp new file mode 100644 index 00000000000..96b7de8ed25 --- /dev/null +++ b/core/os/thread_safe.cpp @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* thread_safe.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef THREAD_SAFE_CPP +#define THREAD_SAFE_CPP + +#include "thread_safe.h" + +static thread_local bool current_thread_safe_for_nodes = false; + +bool is_current_thread_safe_for_nodes() { + return current_thread_safe_for_nodes; +} + +void set_current_thread_safe_for_nodes(bool p_safe) { + current_thread_safe_for_nodes = p_safe; +} + +#endif // THREAD_SAFE_CPP diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h index ac8734b6c16..042a0b7d987 100644 --- a/core/os/thread_safe.h +++ b/core/os/thread_safe.h @@ -38,4 +38,7 @@ #define _THREAD_SAFE_LOCK_ _thread_safe_.lock(); #define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock(); +bool is_current_thread_safe_for_nodes(); +void set_current_thread_safe_for_nodes(bool p_safe); + #endif // THREAD_SAFE_H diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 719de9dc810..0e9ff183556 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -59,6 +59,7 @@ void TilesEditorPlugin::_pattern_preview_done() { void TilesEditorPlugin::_thread_func(void *ud) { TilesEditorPlugin *te = static_cast(ud); + set_current_thread_safe_for_nodes(true); te->_thread(); } diff --git a/main/main.cpp b/main/main.cpp index 17e4f69ef28..94fb7ecdfad 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -512,6 +512,7 @@ void Main::print_help(const char *p_binary) { // are initialized here. This also combines `Main::setup2()` initialization. Error Main::test_setup() { Thread::make_main_thread(); + set_current_thread_safe_for_nodes(true); OS::get_singleton()->initialize(); @@ -723,6 +724,7 @@ int Main::test_entrypoint(int argc, char *argv[], bool &tests_need_run) { Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_phase) { Thread::make_main_thread(); + set_current_thread_safe_for_nodes(true); OS::get_singleton()->initialize(); @@ -1990,6 +1992,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph engine->startup_benchmark_end_measure(); // core Thread::release_main_thread(); // If setup2() is called from another thread, that one will become main thread, so preventively release this one. + set_current_thread_safe_for_nodes(false); if (p_second_phase) { return setup2(); @@ -2055,6 +2058,7 @@ error: Error Main::setup2() { Thread::make_main_thread(); // Make whatever thread call this the main thread. + set_current_thread_safe_for_nodes(true); // Print engine name and version print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE)); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 185db1b972c..4ae67d30dd3 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -2691,9 +2691,11 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { } void RichTextLabel::_thread_function(void *p_userdata) { + set_current_thread_safe_for_nodes(true); _process_line_caches(); updating.store(false); call_deferred(SNAME("thread_end")); + set_current_thread_safe_for_nodes(false); } void RichTextLabel::_thread_end() { diff --git a/scene/main/node.h b/scene/main/node.h index 4d4e71ee566..ecc830949ec 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -539,7 +539,7 @@ public: if (current_process_thread_group == nullptr) { // Not thread processing. Only accessible if node is outside the scene tree, // if accessing from the main thread or being loaded. - return !data.inside_tree || Thread::is_main_thread() || ResourceLoader::is_within_load(); + return !data.inside_tree || is_current_thread_safe_for_nodes(); } else { // Thread processing return current_process_thread_group == data.process_thread_group_owner; @@ -548,7 +548,7 @@ public: _FORCE_INLINE_ bool is_readable_from_caller_thread() const { if (current_process_thread_group == nullptr) { - return Thread::is_main_thread() || ResourceLoader::is_within_load(); + return Thread::is_main_thread() || is_current_thread_safe_for_nodes(); } else { return true; } @@ -727,8 +727,8 @@ Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) #ifdef DEBUG_ENABLED #define ERR_THREAD_GUARD ERR_FAIL_COND_MSG(!is_accessible_from_caller_thread(), "Caller thread can't call this function in this node. Use call_deferred() or call_thread_group() instead."); #define ERR_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_accessible_from_caller_thread(), (m_ret), "Caller thread can't call this function in this node. Use call_deferred() or call_thread_group() instead.") -#define ERR_MAIN_THREAD_GUARD 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."); -#define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !Thread::is_main_thread(), (m_ret), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.") +#define ERR_MAIN_THREAD_GUARD ERR_FAIL_COND_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), "This function in this node can only be accessed from the main thread. Use call_deferred() instead."); +#define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), (m_ret), "This function in this node can only be accessed from the main thread. Use call_deferred() instead.") #define ERR_READ_THREAD_GUARD ERR_FAIL_COND_MSG(!is_readable_from_caller_thread(), "This function in this node can only be accessed from either the main thread or a thread group. Use call_deferred() instead.") #define ERR_READ_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_readable_from_caller_thread(), (m_ret), "This function in this node can only be accessed from either the main thread or a thread group. Use call_deferred() instead.") #else