From 7c26d9371bfc2c75713c83c801c1dff16f0396ae Mon Sep 17 00:00:00 2001 From: Danielle Jenkins Date: Fri, 6 Jun 2025 00:38:12 -0700 Subject: [PATCH 1/3] Fix thread safety issue in resource changed callbacks during imports Use call_deferred() instead of direct call() in ResourceLoader::resource_changed_emit() to ensure resource change callbacks execute on the main thread. This prevents segfaults when font imports trigger node operations from background threads. --- core/io/resource_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 42b6c3c1f460..b098e5932fde 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -968,7 +968,7 @@ void ResourceLoader::resource_changed_emit(Resource *p_source) { for (const ThreadLoadTask::ResourceChangedConnection &rcc : curr_load_task->resource_changed_connections) { if (unlikely(rcc.source == p_source)) { - rcc.callable.call(); + rcc.callable.call_deferred(); } } } From adba69874b4ecebea5d75f7ae38c7859c94e811e Mon Sep 17 00:00:00 2001 From: Danielle Jenkins Date: Sun, 11 Jan 2026 23:10:47 -0800 Subject: [PATCH 2/3] Fix race condition in FontFile data access during threaded imports Add mutex protection to FontFile data access methods to prevent race conditions when multiple import threads access the same cached FontFile simultaneously. Use data_external flag to track whether data came from set_data_ptr() vs set_data() to avoid unnecessary PackedByteArray COW copies. --- scene/resources/font.cpp | 24 +++++++++++++++++++----- scene/resources/font.h | 3 +++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 5e8cc4dc9f36..881fd93f70d7 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -591,6 +591,8 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index, int p_make_linked_f if (p_make_linked_from >= 0 && p_make_linked_from != p_cache_index && p_make_linked_from < cache.size()) { cache.write[p_cache_index] = TS->create_font_linked_variation(cache[p_make_linked_from]); } else { + MutexLock lock(data_mutex); + cache.write[p_cache_index] = TS->create_font(); TS->font_set_data_ptr(cache[p_cache_index], data_ptr, data_size); TS->font_set_antialiasing(cache[p_cache_index], antialiasing); @@ -1408,9 +1410,13 @@ void FontFile::_get_property_list(List *p_list) const { void FontFile::reset_state() { _clear_cache(); - data.clear(); - data_ptr = nullptr; - data_size = 0; + { + MutexLock lock(data_mutex); + data = PackedByteArray(); + data_ptr = nullptr; + data_size = 0; + data_external = false; + } cache.clear(); antialiasing = TextServer::FONT_ANTIALIASING_GRAY; @@ -2070,9 +2076,12 @@ Error FontFile::load_dynamic_font(const String &p_path) { } void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { - data.clear(); + MutexLock lock(data_mutex); + + data = PackedByteArray(); data_ptr = p_data; data_size = p_size; + data_external = true; for (int i = 0; i < cache.size(); i++) { if (cache[i].is_valid()) { @@ -2082,9 +2091,12 @@ void FontFile::set_data_ptr(const uint8_t *p_data, size_t p_size) { } void FontFile::set_data(const PackedByteArray &p_data) { + MutexLock lock(data_mutex); + data = p_data; data_ptr = data.ptr(); data_size = data.size(); + data_external = false; for (int i = 0; i < cache.size(); i++) { if (cache[i].is_valid()) { @@ -2094,7 +2106,9 @@ void FontFile::set_data(const PackedByteArray &p_data) { } PackedByteArray FontFile::get_data() const { - if (unlikely((size_t)data.size() != data_size)) { + MutexLock lock(data_mutex); + + if (unlikely(data_external && data.is_empty() && data_ptr && data_size > 0)) { data.resize(data_size); memcpy(data.ptrw(), data_ptr, data_size); } diff --git a/scene/resources/font.h b/scene/resources/font.h index 08e140d8ff40..9331b53a2bef 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -31,6 +31,7 @@ #pragma once #include "core/io/resource.h" +#include "core/os/mutex.h" #include "core/templates/lru.h" #include "scene/resources/texture.h" #include "servers/text_server.h" @@ -185,8 +186,10 @@ class FontFile : public Font { RES_BASE_EXTENSION("fontdata"); // Font source data. + mutable Mutex data_mutex; const uint8_t *data_ptr = nullptr; size_t data_size = 0; + bool data_external = false; mutable PackedByteArray data; TextServer::FontAntialiasing antialiasing = TextServer::FONT_ANTIALIASING_GRAY; From 3542546d52723fd06388f498db266c70f4074100 Mon Sep 17 00:00:00 2001 From: Novelty Date: Wed, 14 Jan 2026 13:21:12 -0800 Subject: [PATCH 3/3] Remove include from scene/resources/font.h Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> --- scene/resources/font.h | 1 - 1 file changed, 1 deletion(-) diff --git a/scene/resources/font.h b/scene/resources/font.h index 9331b53a2bef..606a7b3ccaa6 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -31,7 +31,6 @@ #pragma once #include "core/io/resource.h" -#include "core/os/mutex.h" #include "core/templates/lru.h" #include "scene/resources/texture.h" #include "servers/text_server.h"