summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/project_settings.cpp3
-rw-r--r--core/core_bind.cpp15
-rw-r--r--core/core_bind.h11
-rw-r--r--core/io/image.cpp24
-rw-r--r--core/io/image.h1
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/math/a_star.cpp16
-rw-r--r--core/math/a_star.h4
-rw-r--r--core/math/a_star_grid_2d.cpp8
-rw-r--r--core/math/a_star_grid_2d.h2
-rw-r--r--core/object/object.cpp11
-rw-r--r--core/object/script_language.cpp2
-rw-r--r--core/object/script_language.h2
-rw-r--r--core/object/script_language_extension.h2
-rw-r--r--core/object/worker_thread_pool.cpp178
-rw-r--r--core/object/worker_thread_pool.h28
-rw-r--r--core/register_core_types.cpp8
-rw-r--r--core/variant/dictionary.cpp59
-rw-r--r--core/variant/dictionary.h1
-rw-r--r--core/variant/variant_setget.cpp44
20 files changed, 293 insertions, 128 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 32f36e01f9..7951ee9edd 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1016,7 +1016,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
}
}
// Check for the existence of a csproj file.
- if (_csproj_exists(p_path.get_base_dir())) {
+ if (_csproj_exists(get_resource_path())) {
// If there is a csproj file, add the C# feature if it doesn't already exist.
if (!project_features.has("C#")) {
project_features.append("C#");
@@ -1574,6 +1574,7 @@ ProjectSettings::ProjectSettings() {
ProjectSettings::ProjectSettings(const String &p_path) {
if (load_custom(p_path) == OK) {
+ resource_path = p_path.get_base_dir();
project_loaded = true;
}
}
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index b27981d56b..bbfbb6e3cd 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1419,6 +1419,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const {
}
}
+ClassDB::APIType ClassDB::class_get_api_type(const StringName &p_class) const {
+ ::ClassDB::APIType api_type = ::ClassDB::get_api_type(p_class);
+ return (APIType)api_type;
+}
+
bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const {
return ::ClassDB::has_signal(p_class, p_signal);
}
@@ -1615,7 +1620,7 @@ void ClassDB::get_argument_options(const StringName &p_function, int p_idx, List
pf == "class_has_method" || pf == "class_get_method_list" ||
pf == "class_get_integer_constant_list" || pf == "class_has_integer_constant" || pf == "class_get_integer_constant" ||
pf == "class_has_enum" || pf == "class_get_enum_list" || pf == "class_get_enum_constants" || pf == "class_get_integer_constant_enum" ||
- pf == "is_class_enabled" || pf == "is_class_enum_bitfield");
+ pf == "is_class_enabled" || pf == "is_class_enum_bitfield" || pf == "class_get_api_type");
}
if (first_argument_is_class || pf == "is_parent_class") {
for (const String &E : get_class_list()) {
@@ -1636,6 +1641,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("can_instantiate", "class"), &ClassDB::can_instantiate);
::ClassDB::bind_method(D_METHOD("instantiate", "class"), &ClassDB::instantiate);
+ ::ClassDB::bind_method(D_METHOD("class_get_api_type", "class"), &ClassDB::class_get_api_type);
+
::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal);
::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal);
::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
@@ -1669,6 +1676,12 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("is_class_enum_bitfield", "class", "enum", "no_inheritance"), &ClassDB::is_class_enum_bitfield, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);
+
+ BIND_ENUM_CONSTANT(API_CORE);
+ BIND_ENUM_CONSTANT(API_EDITOR);
+ BIND_ENUM_CONSTANT(API_EXTENSION);
+ BIND_ENUM_CONSTANT(API_EDITOR_EXTENSION);
+ BIND_ENUM_CONSTANT(API_NONE);
}
} // namespace special
diff --git a/core/core_bind.h b/core/core_bind.h
index 7e2686c848..2a31e64425 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -447,6 +447,14 @@ protected:
static void _bind_methods();
public:
+ enum APIType {
+ API_CORE,
+ API_EDITOR,
+ API_EXTENSION,
+ API_EDITOR_EXTENSION,
+ API_NONE,
+ };
+
PackedStringArray get_class_list() const;
PackedStringArray get_inheriters_from_class(const StringName &p_class) const;
StringName get_parent_class(const StringName &p_class) const;
@@ -455,6 +463,7 @@ public:
bool can_instantiate(const StringName &p_class) const;
Variant instantiate(const StringName &p_class) const;
+ APIType class_get_api_type(const StringName &p_class) const;
bool class_has_signal(const StringName &p_class, const StringName &p_signal) const;
Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const;
TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
@@ -634,4 +643,6 @@ VARIANT_ENUM_CAST(core_bind::Geometry2D::PolyEndType);
VARIANT_ENUM_CAST(core_bind::Thread::Priority);
+VARIANT_ENUM_CAST(core_bind::special::ClassDB::APIType);
+
#endif // CORE_BIND_H
diff --git a/core/io/image.cpp b/core/io/image.cpp
index fcbe483e38..aa391b77dd 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -866,12 +866,10 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_
template <int CC, typename T>
static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) {
- enum {
- FRAC_BITS = 8,
- FRAC_LEN = (1 << FRAC_BITS),
- FRAC_HALF = (FRAC_LEN >> 1),
- FRAC_MASK = FRAC_LEN - 1
- };
+ constexpr uint32_t FRAC_BITS = 8;
+ constexpr uint32_t FRAC_LEN = (1 << FRAC_BITS);
+ constexpr uint32_t FRAC_HALF = (FRAC_LEN >> 1);
+ constexpr uint32_t FRAC_MASK = FRAC_LEN - 1;
for (uint32_t i = 0; i < p_dst_height; i++) {
// Add 0.5 in order to interpolate based on pixel center
@@ -2751,6 +2749,19 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
} break;
+ case COMPRESS_S3TC: {
+ // BC3 is unsupported currently.
+ if ((p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) {
+ Error result = _image_compress_bc_rd_func(this, p_channels);
+
+ // If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme.
+ if (result == OK) {
+ return OK;
+ }
+ }
+
+ } break;
+
default: {
}
}
@@ -3138,6 +3149,7 @@ void (*Image::_image_compress_etc1_func)(Image *) = nullptr;
void (*Image::_image_compress_etc2_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_compress_astc_func)(Image *, Image::ASTCFormat) = nullptr;
Error (*Image::_image_compress_bptc_rd_func)(Image *, Image::UsedChannels) = nullptr;
+Error (*Image::_image_compress_bc_rd_func)(Image *, Image::UsedChannels) = nullptr;
void (*Image::_image_decompress_bc)(Image *) = nullptr;
void (*Image::_image_decompress_bptc)(Image *) = nullptr;
void (*Image::_image_decompress_etc1)(Image *) = nullptr;
diff --git a/core/io/image.h b/core/io/image.h
index 4461ae71a6..78757246e0 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -160,6 +160,7 @@ public:
static void (*_image_compress_astc_func)(Image *, ASTCFormat p_format);
static Error (*_image_compress_bptc_rd_func)(Image *, UsedChannels p_channels);
+ static Error (*_image_compress_bc_rd_func)(Image *, UsedChannels p_channels);
static void (*_image_decompress_bc)(Image *);
static void (*_image_decompress_bptc)(Image *);
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index f29f9eef98..94f6caf6eb 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -860,7 +860,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
}
}
core_bind::Semaphore done;
- MessageQueue::get_main_singleton()->push_callable(callable_mp(&done, &core_bind::Semaphore::post));
+ MessageQueue::get_main_singleton()->push_callable(callable_mp(&done, &core_bind::Semaphore::post).bind(1));
done.wait();
}
}
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index c53fd3d330..c85201a973 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -319,11 +319,11 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
return closest_point;
}
-bool AStar3D::_solve(Point *begin_point, Point *end_point) {
+bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_path) {
last_closest_point = nullptr;
pass++;
- if (!end_point->enabled) {
+ if (!end_point->enabled && !p_allow_partial_path) {
return false;
}
@@ -443,7 +443,7 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
Point *begin_point = a;
Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector3>();
@@ -497,7 +497,7 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
Point *begin_point = a;
Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<int64_t>();
@@ -726,7 +726,7 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<Vector2>();
@@ -780,7 +780,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<int64_t>();
@@ -816,11 +816,11 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
return path;
}
-bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
+bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path) {
astar.last_closest_point = nullptr;
astar.pass++;
- if (!end_point->enabled) {
+ if (!end_point->enabled && !p_allow_partial_path) {
return false;
}
diff --git a/core/math/a_star.h b/core/math/a_star.h
index 143a3bec61..cbaafc1018 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -115,7 +115,7 @@ class AStar3D : public RefCounted {
HashSet<Segment, Segment> segments;
Point *last_closest_point = nullptr;
- bool _solve(Point *begin_point, Point *end_point);
+ bool _solve(Point *begin_point, Point *end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();
@@ -171,7 +171,7 @@ class AStar2D : public RefCounted {
GDCLASS(AStar2D, RefCounted);
AStar3D astar;
- bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point);
+ bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index c40ee5b4d7..7e0e982c62 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -491,11 +491,11 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
}
}
-bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
+bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path) {
last_closest_point = nullptr;
pass++;
- if (_get_solid_unchecked(p_end_point->id)) {
+ if (_get_solid_unchecked(p_end_point->id) && !p_allow_partial_path) {
return false;
}
@@ -647,7 +647,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
Point *begin_point = a;
Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector2>();
@@ -700,7 +700,7 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
Point *begin_point = a;
Point *end_point = b;
- bool found_route = _solve(begin_point, end_point);
+ bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
if (!p_allow_partial_path || last_closest_point == nullptr) {
return TypedArray<Vector2i>();
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index f5ac472f09..0536b8109b 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -159,8 +159,8 @@ private: // Internal routines.
void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors);
Point *_jump(Point *p_from, Point *p_to);
+ bool _solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path);
Point *_forced_successor(int32_t p_x, int32_t p_y, int32_t p_dx, int32_t p_dy, bool p_inclusive = false);
- bool _solve(Point *p_begin_point, Point *p_end_point);
protected:
static void _bind_methods();
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 000d5328b4..da3ca6bc61 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -44,14 +44,17 @@
#ifdef DEBUG_ENABLED
struct _ObjectDebugLock {
- Object *obj;
+ ObjectID obj_id;
_ObjectDebugLock(Object *p_obj) {
- obj = p_obj;
- obj->_lock_index.ref();
+ obj_id = p_obj->get_instance_id();
+ p_obj->_lock_index.ref();
}
~_ObjectDebugLock() {
- obj->_lock_index.unref();
+ Object *obj_ptr = ObjectDB::get_instance(obj_id);
+ if (likely(obj_ptr)) {
+ obj_ptr->_lock_index.unref();
+ }
}
};
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index d5b7bc768d..d2fc7392c8 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -174,6 +174,8 @@ void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_tool"), &Script::is_tool);
ClassDB::bind_method(D_METHOD("is_abstract"), &Script::is_abstract);
+ ClassDB::bind_method(D_METHOD("get_rpc_config"), &Script::get_rpc_config);
+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code");
}
diff --git a/core/object/script_language.h b/core/object/script_language.h
index d9e2ab1d3c..d0023d70e8 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -182,7 +182,7 @@ public:
virtual bool is_placeholder_fallback_enabled() const { return false; }
- virtual const Variant get_rpc_config() const = 0;
+ virtual Variant get_rpc_config() const = 0;
Script() {}
};
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index c9344f5799..bc773c5ad3 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -205,7 +205,7 @@ public:
GDVIRTUAL0RC(Variant, _get_rpc_config)
- virtual const Variant get_rpc_config() const override {
+ virtual Variant get_rpc_config() const override {
Variant ret;
GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret);
return ret;
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index cf396c2676..08903d6196 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -180,13 +180,17 @@ void WorkerThreadPool::_process_task(Task *p_task) {
void WorkerThreadPool::_thread_function(void *p_user) {
ThreadData *thread_data = (ThreadData *)p_user;
+
while (true) {
Task *task_to_process = nullptr;
{
MutexLock lock(singleton->task_mutex);
- if (singleton->exit_threads) {
- return;
+
+ bool exit = singleton->_handle_runlevel(thread_data, lock);
+ if (unlikely(exit)) {
+ break;
}
+
thread_data->signaled = false;
if (singleton->task_queue.first()) {
@@ -194,7 +198,6 @@ void WorkerThreadPool::_thread_function(void *p_user) {
singleton->task_queue.remove(singleton->task_queue.first());
} else {
thread_data->cond_var.wait(lock);
- DEV_ASSERT(singleton->exit_threads || thread_data->signaled);
}
}
@@ -204,19 +207,24 @@ void WorkerThreadPool::_thread_function(void *p_user) {
}
}
-void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority) {
+void WorkerThreadPool::_post_tasks(Task **p_tasks, uint32_t p_count, bool p_high_priority, MutexLock<BinaryMutex> &p_lock) {
// Fall back to processing on the calling thread if there are no worker threads.
// Separated into its own variable to make it easier to extend this logic
// in custom builds.
bool process_on_calling_thread = threads.size() == 0;
if (process_on_calling_thread) {
- task_mutex.unlock();
+ p_lock.temp_unlock();
for (uint32_t i = 0; i < p_count; i++) {
_process_task(p_tasks[i]);
}
+ p_lock.temp_relock();
return;
}
+ while (runlevel == RUNLEVEL_EXIT_LANGUAGES) {
+ control_cond_var.wait(p_lock);
+ }
+
uint32_t to_process = 0;
uint32_t to_promote = 0;
@@ -238,8 +246,6 @@ void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count,
}
_notify_threads(caller_pool_thread, to_process, to_promote);
-
- task_mutex.unlock();
}
void WorkerThreadPool::_notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count) {
@@ -323,9 +329,8 @@ WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *
}
WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description) {
- ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a task because the WorkerThreadPool is either not initialized yet or already terminated.");
+ MutexLock<BinaryMutex> lock(task_mutex);
- task_mutex.lock();
// Get a free task
Task *task = task_allocator.alloc();
TaskID id = last_task++;
@@ -337,7 +342,7 @@ WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable,
task->template_userdata = p_template_userdata;
tasks.insert(id, task);
- _post_tasks_and_unlock(&task, 1, p_high_priority);
+ _post_tasks(&task, 1, p_high_priority, lock);
return id;
}
@@ -444,22 +449,34 @@ void WorkerThreadPool::_unlock_unlockable_mutexes() {
void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) {
// Keep processing tasks until the condition to stop waiting is met.
-#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed)
-
while (true) {
Task *task_to_process = nullptr;
bool relock_unlockables = false;
{
MutexLock lock(task_mutex);
+
bool was_signaled = p_caller_pool_thread->signaled;
p_caller_pool_thread->signaled = false;
- if (IS_WAIT_OVER) {
- if (unlikely(p_task == ThreadData::YIELDING)) {
+ bool exit = _handle_runlevel(p_caller_pool_thread, lock);
+ if (unlikely(exit)) {
+ break;
+ }
+
+ bool wait_is_over = false;
+ if (unlikely(p_task == ThreadData::YIELDING)) {
+ if (p_caller_pool_thread->yield_is_over) {
p_caller_pool_thread->yield_is_over = false;
+ wait_is_over = true;
}
+ } else {
+ if (p_task->completed) {
+ wait_is_over = true;
+ }
+ }
- if (!exit_threads && was_signaled) {
+ if (wait_is_over) {
+ if (was_signaled) {
// This thread was awaken for some additional reason, but it's about to exit.
// Let's find out what may be pending and forward the requests.
uint32_t to_process = task_queue.first() ? 1 : 0;
@@ -474,28 +491,26 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
break;
}
- if (!exit_threads) {
- if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
- if (_try_promote_low_priority_task()) {
- _notify_threads(p_caller_pool_thread, 1, 0);
- }
+ if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
+ if (_try_promote_low_priority_task()) {
+ _notify_threads(p_caller_pool_thread, 1, 0);
}
+ }
- if (singleton->task_queue.first()) {
- task_to_process = task_queue.first()->self();
- task_queue.remove(task_queue.first());
- }
+ if (singleton->task_queue.first()) {
+ task_to_process = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ }
- if (!task_to_process) {
- p_caller_pool_thread->awaited_task = p_task;
+ if (!task_to_process) {
+ p_caller_pool_thread->awaited_task = p_task;
- _unlock_unlockable_mutexes();
- relock_unlockables = true;
- p_caller_pool_thread->cond_var.wait(lock);
+ _unlock_unlockable_mutexes();
+ relock_unlockables = true;
- DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER);
- p_caller_pool_thread->awaited_task = nullptr;
- }
+ p_caller_pool_thread->cond_var.wait(lock);
+
+ p_caller_pool_thread->awaited_task = nullptr;
}
}
@@ -509,16 +524,65 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
}
}
+void WorkerThreadPool::_switch_runlevel(Runlevel p_runlevel) {
+ DEV_ASSERT(p_runlevel > runlevel);
+ runlevel = p_runlevel;
+ memset(&runlevel_data, 0, sizeof(runlevel_data));
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ threads[i].cond_var.notify_one();
+ threads[i].signaled = true;
+ }
+ control_cond_var.notify_all();
+}
+
+// Returns whether threads have to exit. This may perform the check about handling needed.
+bool WorkerThreadPool::_handle_runlevel(ThreadData *p_thread_data, MutexLock<BinaryMutex> &p_lock) {
+ bool exit = false;
+ switch (runlevel) {
+ case RUNLEVEL_NORMAL: {
+ } break;
+ case RUNLEVEL_PRE_EXIT_LANGUAGES: {
+ if (!p_thread_data->pre_exited_languages) {
+ if (!task_queue.first() && !low_priority_task_queue.first()) {
+ p_thread_data->pre_exited_languages = true;
+ runlevel_data.pre_exit_languages.num_idle_threads++;
+ control_cond_var.notify_all();
+ }
+ }
+ } break;
+ case RUNLEVEL_EXIT_LANGUAGES: {
+ if (!p_thread_data->exited_languages) {
+ p_lock.temp_unlock();
+ ScriptServer::thread_exit();
+ p_lock.temp_relock();
+ p_thread_data->exited_languages = true;
+ runlevel_data.exit_languages.num_exited_threads++;
+ control_cond_var.notify_all();
+ }
+ } break;
+ case RUNLEVEL_EXIT: {
+ exit = true;
+ } break;
+ }
+ return exit;
+}
+
void WorkerThreadPool::yield() {
int th_index = get_thread_index();
ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread.");
_wait_collaboratively(&threads[th_index], ThreadData::YIELDING);
- // If this long-lived task started before the scripting server was initialized,
- // now is a good time to have scripting languages ready for the current thread.
- // Otherwise, such a piece of setup won't happen unless another task has been
- // run during the collaborative wait.
- ScriptServer::thread_enter();
+ task_mutex.lock();
+ if (runlevel < RUNLEVEL_EXIT_LANGUAGES) {
+ // If this long-lived task started before the scripting server was initialized,
+ // now is a good time to have scripting languages ready for the current thread.
+ // Otherwise, such a piece of setup won't happen unless another task has been
+ // run during the collaborative wait.
+ task_mutex.unlock();
+ ScriptServer::thread_enter();
+ } else {
+ task_mutex.unlock();
+ }
}
void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
@@ -543,13 +607,13 @@ void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
- ERR_FAIL_COND_V_MSG(threads.is_empty(), INVALID_TASK_ID, "Can't add a group task because the WorkerThreadPool is either not initialized yet or already terminated.");
ERR_FAIL_COND_V(p_elements < 0, INVALID_TASK_ID);
if (p_tasks < 0) {
p_tasks = MAX(1u, threads.size());
}
- task_mutex.lock();
+ MutexLock<BinaryMutex> lock(task_mutex);
+
Group *group = group_allocator.alloc();
GroupID id = last_task++;
group->max = p_elements;
@@ -584,7 +648,7 @@ WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_ca
groups[id] = group;
- _post_tasks_and_unlock(tasks_posted, p_tasks, p_high_priority);
+ _post_tasks(tasks_posted, p_tasks, p_high_priority, lock);
return id;
}
@@ -687,6 +751,9 @@ void WorkerThreadPool::thread_exit_unlock_allowance_zone(uint32_t p_zone_id) {
void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio) {
ERR_FAIL_COND(threads.size() > 0);
+
+ runlevel = RUNLEVEL_NORMAL;
+
if (p_thread_count < 0) {
p_thread_count = OS::get_singleton()->get_default_thread_pool_size();
}
@@ -704,6 +771,26 @@ void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio)
}
}
+void WorkerThreadPool::exit_languages_threads() {
+ if (threads.size() == 0) {
+ return;
+ }
+
+ MutexLock lock(task_mutex);
+
+ // Wait until all threads are idle.
+ _switch_runlevel(RUNLEVEL_PRE_EXIT_LANGUAGES);
+ while (runlevel_data.pre_exit_languages.num_idle_threads != threads.size()) {
+ control_cond_var.wait(lock);
+ }
+
+ // Wait until all threads have detached from scripting languages.
+ _switch_runlevel(RUNLEVEL_EXIT_LANGUAGES);
+ while (runlevel_data.exit_languages.num_exited_threads != threads.size()) {
+ control_cond_var.wait(lock);
+ }
+}
+
void WorkerThreadPool::finish() {
if (threads.size() == 0) {
return;
@@ -716,15 +803,10 @@ void WorkerThreadPool::finish() {
print_error("Task waiting was never re-claimed: " + E->self()->description);
E = E->next();
}
- }
- {
- MutexLock lock(task_mutex);
- exit_threads = true;
- }
- for (ThreadData &data : threads) {
- data.cond_var.notify_one();
+ _switch_runlevel(RUNLEVEL_EXIT);
}
+
for (ThreadData &data : threads) {
data.thread.wait_to_finish();
}
@@ -755,5 +837,5 @@ WorkerThreadPool::WorkerThreadPool() {
}
WorkerThreadPool::~WorkerThreadPool() {
- DEV_ASSERT(threads.size() == 0 && "finish() hasn't been called!");
+ finish();
}
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index 6374dbe8c7..62296ac040 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -114,17 +114,35 @@ private:
Thread thread;
bool signaled : 1;
bool yield_is_over : 1;
+ bool pre_exited_languages : 1;
+ bool exited_languages : 1;
Task *current_task = nullptr;
Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
ConditionVariable cond_var;
ThreadData() :
signaled(false),
- yield_is_over(false) {}
+ yield_is_over(false),
+ pre_exited_languages(false),
+ exited_languages(false) {}
};
TightLocalVector<ThreadData> threads;
- bool exit_threads = false;
+ enum Runlevel {
+ RUNLEVEL_NORMAL,
+ RUNLEVEL_PRE_EXIT_LANGUAGES, // Block adding new tasks
+ RUNLEVEL_EXIT_LANGUAGES, // All threads detach from scripting threads.
+ RUNLEVEL_EXIT,
+ } runlevel = RUNLEVEL_NORMAL;
+ union { // Cleared on every runlevel change.
+ struct {
+ uint32_t num_idle_threads;
+ } pre_exit_languages;
+ struct {
+ uint32_t num_exited_threads;
+ } exit_languages;
+ } runlevel_data;
+ ConditionVariable control_cond_var;
HashMap<Thread::ID, int> thread_ids;
HashMap<
@@ -152,7 +170,7 @@ private:
void _process_task(Task *task);
- void _post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority);
+ void _post_tasks(Task **p_tasks, uint32_t p_count, bool p_high_priority, MutexLock<BinaryMutex> &p_lock);
void _notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count);
bool _try_promote_low_priority_task();
@@ -193,6 +211,9 @@ private:
void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
+ void _switch_runlevel(Runlevel p_runlevel);
+ bool _handle_runlevel(ThreadData *p_thread_data, MutexLock<BinaryMutex> &p_lock);
+
#ifdef THREADS_ENABLED
static uint32_t _thread_enter_unlock_allowance_zone(THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &p_ulock);
#endif
@@ -256,6 +277,7 @@ public:
#endif
void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3);
+ void exit_languages_threads();
void finish();
WorkerThreadPool();
~WorkerThreadPool();
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 220ed9da31..c866ff0415 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -107,6 +107,8 @@ static Time *_time = nullptr;
static core_bind::Geometry2D *_geometry_2d = nullptr;
static core_bind::Geometry3D *_geometry_3d = nullptr;
+static WorkerThreadPool *worker_thread_pool = nullptr;
+
extern Mutex _global_mutex;
static GDExtensionManager *gdextension_manager = nullptr;
@@ -295,6 +297,8 @@ void register_core_types() {
GDREGISTER_NATIVE_STRUCT(AudioFrame, "float left;float right");
GDREGISTER_NATIVE_STRUCT(ScriptLanguageExtensionProfilingInfo, "StringName signature;uint64_t call_count;uint64_t total_time;uint64_t self_time");
+ worker_thread_pool = memnew(WorkerThreadPool);
+
OS::get_singleton()->benchmark_end_measure("Core", "Register Types");
}
@@ -345,7 +349,7 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
- Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", WorkerThreadPool::get_singleton()));
+ Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool));
OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons");
}
@@ -378,6 +382,8 @@ void unregister_core_types() {
// Destroy singletons in reverse order to ensure dependencies are not broken.
+ memdelete(worker_thread_pool);
+
memdelete(_engine_debugger);
memdelete(_marshalls);
memdelete(_classdb);
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 0754814d35..2db754438f 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -83,35 +83,64 @@ Variant Dictionary::get_value_at_index(int p_index) const {
return Variant();
}
+// WARNING: This operator does not validate the value type. For scripting/extensions this is
+// done in `variant_setget.cpp`. Consider using `set()` if the data might be invalid.
Variant &Dictionary::operator[](const Variant &p_key) {
- if (unlikely(_p->read_only)) {
- if (likely(_p->variant_map.has(p_key))) {
- *_p->read_only = _p->variant_map[p_key];
+ Variant key = p_key;
+ if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
+ if (unlikely(!_p->typed_fallback)) {
+ _p->typed_fallback = memnew(Variant);
+ }
+ VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
+ return *_p->typed_fallback;
+ } else if (unlikely(_p->read_only)) {
+ if (likely(_p->variant_map.has(key))) {
+ *_p->read_only = _p->variant_map[key];
} else {
- *_p->read_only = Variant();
+ VariantInternal::initialize(_p->read_only, _p->typed_value.type);
}
-
return *_p->read_only;
} else {
- return _p->variant_map[p_key];
+ if (unlikely(!_p->variant_map.has(key))) {
+ VariantInternal::initialize(&_p->variant_map[key], _p->typed_value.type);
+ }
+ return _p->variant_map[key];
}
}
const Variant &Dictionary::operator[](const Variant &p_key) const {
- // Will not insert key, so no conversion is necessary.
- return _p->variant_map[p_key];
+ Variant key = p_key;
+ if (unlikely(!_p->typed_key.validate(key, "use `operator[]`"))) {
+ if (unlikely(!_p->typed_fallback)) {
+ _p->typed_fallback = memnew(Variant);
+ }
+ VariantInternal::initialize(_p->typed_fallback, _p->typed_value.type);
+ return *_p->typed_fallback;
+ } else {
+ // Will not insert key, so no initialization is necessary.
+ return _p->variant_map[key];
+ }
}
const Variant *Dictionary::getptr(const Variant &p_key) const {
- HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(p_key));
+ Variant key = p_key;
+ if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
+ return nullptr;
+ }
+ HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::ConstIterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
return &E->value;
}
+// WARNING: This method does not validate the value type.
Variant *Dictionary::getptr(const Variant &p_key) {
- HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(p_key));
+ Variant key = p_key;
+ if (unlikely(!_p->typed_key.validate(key, "getptr"))) {
+ return nullptr;
+ }
+ HashMap<Variant, Variant, VariantHasher, StringLikeVariantComparator>::Iterator E(_p->variant_map.find(key));
if (!E) {
return nullptr;
}
@@ -158,6 +187,16 @@ Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) {
return *result;
}
+bool Dictionary::set(const Variant &p_key, const Variant &p_value) {
+ ERR_FAIL_COND_V_MSG(_p->read_only, false, "Dictionary is in read-only state.");
+ Variant key = p_key;
+ ERR_FAIL_COND_V(!_p->typed_key.validate(key, "set"), false);
+ Variant value = p_value;
+ ERR_FAIL_COND_V(!_p->typed_value.validate(value, "set"), false);
+ _p->variant_map[key] = value;
+ return true;
+}
+
int Dictionary::size() const {
return _p->variant_map.size();
}
diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h
index 57fbefc8f2..5f3ce40219 100644
--- a/core/variant/dictionary.h
+++ b/core/variant/dictionary.h
@@ -59,6 +59,7 @@ public:
Variant get_valid(const Variant &p_key) const;
Variant get(const Variant &p_key, const Variant &p_default) const;
Variant get_or_add(const Variant &p_key, const Variant &p_default);
+ bool set(const Variant &p_key, const Variant &p_value);
int size() const;
bool is_empty() const;
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index b60ff83cf1..1652f81d99 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -252,20 +252,7 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool
}
} else if (type == Variant::DICTIONARY) {
Dictionary &dict = *VariantGetInternalPtr<Dictionary>::get_ptr(this);
-
- if (dict.is_read_only()) {
- r_valid = false;
- return;
- }
-
- Variant *v = dict.getptr(p_member);
- if (v) {
- *v = p_value;
- } else {
- dict[p_member] = p_value;
- }
-
- r_valid = true;
+ r_valid = dict.set(p_member, p_value);
} else {
r_valid = false;
}
@@ -721,26 +708,16 @@ struct VariantIndexedSetGet_Dictionary {
PtrToArg<Variant>::encode(*ptr, member);
}
static void set(Variant *base, int64_t index, const Variant *value, bool *valid, bool *oob) {
- if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
- *valid = false;
- *oob = true;
- return;
- }
- (*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
- *oob = false;
- *valid = true;
+ *valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
+ *oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void validated_set(Variant *base, int64_t index, const Variant *value, bool *oob) {
- if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
- *oob = true;
- return;
- }
- (*VariantGetInternalPtr<Dictionary>::get_ptr(base))[index] = *value;
- *oob = false;
+ VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(index, *value);
+ *oob = VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only();
}
static void ptr_set(void *base, int64_t index, const void *member) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
- v[index] = PtrToArg<Variant>::convert(member);
+ v.set(index, PtrToArg<Variant>::convert(member));
}
static Variant::Type get_index_type() { return Variant::NIL; }
static uint32_t get_index_usage() { return PROPERTY_USAGE_DEFAULT; }
@@ -1010,16 +987,11 @@ struct VariantKeyedSetGetDictionary {
PtrToArg<Variant>::encode(*ptr, value);
}
static void set(Variant *base, const Variant *key, const Variant *value, bool *r_valid) {
- if (VariantGetInternalPtr<Dictionary>::get_ptr(base)->is_read_only()) {
- *r_valid = false;
- return;
- }
- (*VariantGetInternalPtr<Dictionary>::get_ptr(base))[*key] = *value;
- *r_valid = true;
+ *r_valid = VariantGetInternalPtr<Dictionary>::get_ptr(base)->set(*key, *value);
}
static void ptr_set(void *base, const void *key, const void *value) {
Dictionary &v = *reinterpret_cast<Dictionary *>(base);
- v[PtrToArg<Variant>::convert(key)] = PtrToArg<Variant>::convert(value);
+ v.set(PtrToArg<Variant>::convert(key), PtrToArg<Variant>::convert(value));
}
static bool has(const Variant *base, const Variant *key, bool *r_valid) {