summaryrefslogtreecommitdiffstats
path: root/core/core_bind.cpp
diff options
context:
space:
mode:
authorPedro J. Estébanez <pedrojrulez@gmail.com>2023-01-28 13:34:06 +0100
committerPedro J. Estébanez <pedrojrulez@gmail.com>2023-05-12 19:04:57 +0200
commit6189ab5291e54dfe090a081cf292e3d6f9c6b8b1 (patch)
treeb8981fc927de9068a6e3dfb4df2ad70f47c44fa5 /core/core_bind.cpp
parent20ed51a9129f97bb8d001262155fb3ccfc1e3c89 (diff)
downloadredot-engine-6189ab5291e54dfe090a081cf292e3d6f9c6b8b1.tar.gz
Robustify multi-threading primitives
Diffstat (limited to 'core/core_bind.cpp')
-rw-r--r--core/core_bind.cpp24
1 files changed, 20 insertions, 4 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8fa7aad0ac..8afca5a2df 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1178,14 +1178,30 @@ void Thread::_start_func(void *ud) {
String func_name = t->target_callable.is_custom() ? t->target_callable.get_custom()->get_as_text() : String(t->target_callable.get_method());
::Thread::set_name(func_name);
+ // To avoid a circular reference between the thread and the script which can possibly contain a reference
+ // to the thread, we will do the call (keeping a reference up to that point) and then break chains with it.
+ // When the call returns, we will reference the thread again if possible.
+ ObjectID th_instance_id = t->get_instance_id();
+ Callable target_callable = t->target_callable;
+ t = Ref<Thread>();
+
Callable::CallError ce;
- t->target_callable.callp(nullptr, 0, t->ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
+ Variant ret;
+ target_callable.callp(nullptr, 0, ret, ce);
+ // If script properly kept a reference to the thread, we should be able to re-reference it now
+ // (well, or if the call failed, since we had to break chains anyway because the outcome isn't known upfront).
+ t = Ref<Thread>(ObjectDB::get_instance(th_instance_id));
+ if (t.is_valid()) {
+ t->ret = ret;
t->running.clear();
- ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ } else {
+ // We could print a warning here, but the Thread object will be eventually destroyed
+ // noticing wait_to_finish() hasn't been called on it, and it will print a warning itself.
}
- t->running.clear();
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_FAIL_MSG("Could not call function '" + func_name + "' to start thread " + t->get_id() + ": " + Variant::get_callable_error_text(t->target_callable, nullptr, 0, ce) + ".");
+ }
}
Error Thread::start(const Callable &p_callable, Priority p_priority) {