From d8f5f576388195d89f02877f692b33a4f554d015 Mon Sep 17 00:00:00 2001 From: Radiant <69520693+RadiantUwU@users.noreply.github.com> Date: Tue, 26 Nov 2024 22:29:25 +0200 Subject: [PATCH] Thread-safe signalling inside Mutex class. --- core/core_bind.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ core/core_bind.h | 15 ++++++++++++ core/object/object.cpp | 2 +- core/object/object.h | 29 +++++++++++++--------- doc/classes/Mutex.xml | 2 ++ 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 925551d933fe..805118951af4 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1301,6 +1301,61 @@ void Mutex::unlock() { mutex.unlock(); } +bool Mutex::_mt_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) { + MutexLock lock(mutex); + return Object::_disconnect(p_signal, p_callable, p_force); +} + +Error Mutex::emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) { + MutexLock lock(mutex); + return Object::emit_signalp(p_name, p_args, p_argcount); +} + +bool Mutex::has_signal(const StringName &p_name) const { + MutexLock lock(mutex); + return Object::has_signal(p_name); +} + +void Mutex::get_signal_list(List *p_signals) const { + MutexLock lock(mutex); + Object::get_signal_list(p_signals); +} + +void Mutex::get_signal_connection_list(const StringName &p_signal, List *p_connections) const { + MutexLock lock(mutex); + Object::get_signal_connection_list(p_signal, p_connections); +} + +void Mutex::get_all_signal_connections(List *p_connections) const { + MutexLock lock(mutex); + Object::get_all_signal_connections(p_connections); +} + +int Mutex::get_persistent_signal_connection_count() const { + MutexLock lock(mutex); + return Object::get_persistent_signal_connection_count(); +} + +void Mutex::get_signals_connected_to_this(List *p_connections) const { + MutexLock lock(mutex); + Object::get_signals_connected_to_this(p_connections); +} + +Error Mutex::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) { + MutexLock lock(mutex); + return Object::connect(p_signal, p_callable, p_flags); +} + +void Mutex::disconnect(const StringName &p_signal, const Callable &p_callable) { + MutexLock lock(mutex); + Object::disconnect(p_signal, p_callable); +} + +bool Mutex::is_connected(const StringName &p_signal, const Callable &p_callable) const { + MutexLock lock(mutex); + return Object::is_connected(p_signal, p_callable); +} + void Mutex::_bind_methods() { ClassDB::bind_method(D_METHOD("lock"), &Mutex::lock); ClassDB::bind_method(D_METHOD("try_lock"), &Mutex::try_lock); diff --git a/core/core_bind.h b/core/core_bind.h index d013e348bd87..9eee2a7e761f 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -407,10 +407,25 @@ class Mutex : public RefCounted { static void _bind_methods(); +protected: + virtual bool _mt_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false) override; + public: void lock(); bool try_lock(); void unlock(); + + virtual Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) override; + virtual bool has_signal(const StringName &p_name) const override; + virtual void get_signal_list(List *p_signals) const override; + virtual void get_signal_connection_list(const StringName &p_signal, List *p_connections) const override; + virtual void get_all_signal_connections(List *p_connections) const override; + virtual int get_persistent_signal_connection_count() const override; + virtual void get_signals_connected_to_this(List *p_connections) const override; + + virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override; + virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override; + virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override; }; class Semaphore : public RefCounted { diff --git a/core/object/object.cpp b/core/object/object.cpp index ef1ca8132cbd..033d4353eab3 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -2153,7 +2153,7 @@ Object::~Object() { Object *obj = c.callable.get_object(); bool disconnected = false; if (likely(obj)) { - disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true); + disconnected = c.signal.get_object()->_mt_disconnect(c.signal.get_name(), c.callable, true); } if (unlikely(!disconnected)) { // If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here. diff --git a/core/object/object.h b/core/object/object.h index 11b94a7fbf0a..60d1e323d782 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -756,6 +756,11 @@ class Object { bool _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false); + // When a multi-threaded guard is required. + virtual bool _mt_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false) { + return _disconnect(p_signal, p_callable, p_force); + } + #ifdef TOOLS_ENABLED struct VirtualMethodTracker { void **method; @@ -921,18 +926,18 @@ class Object { return emit_signalp(p_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - MTVIRTUAL Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); - MTVIRTUAL bool has_signal(const StringName &p_name) const; - MTVIRTUAL void get_signal_list(List *p_signals) const; - MTVIRTUAL void get_signal_connection_list(const StringName &p_signal, List *p_connections) const; - MTVIRTUAL void get_all_signal_connections(List *p_connections) const; - MTVIRTUAL int get_persistent_signal_connection_count() const; - MTVIRTUAL void get_signals_connected_to_this(List *p_connections) const; - - MTVIRTUAL Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); - MTVIRTUAL void disconnect(const StringName &p_signal, const Callable &p_callable); - MTVIRTUAL bool is_connected(const StringName &p_signal, const Callable &p_callable) const; - MTVIRTUAL bool has_connections(const StringName &p_signal) const; + virtual Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount); + virtual bool has_signal(const StringName &p_name) const; + virtual void get_signal_list(List *p_signals) const; + virtual void get_signal_connection_list(const StringName &p_signal, List *p_connections) const; + virtual void get_all_signal_connections(List *p_connections) const; + virtual int get_persistent_signal_connection_count() const; + virtual void get_signals_connected_to_this(List *p_connections) const; + + virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0); + virtual void disconnect(const StringName &p_signal, const Callable &p_callable); + virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const; + virtual bool has_connections(const StringName &p_signal) const; template void call_deferred(const StringName &p_name, VarArgs... p_args) { diff --git a/doc/classes/Mutex.xml b/doc/classes/Mutex.xml index 4fdf46bb3a78..cdea5bfbdd21 100644 --- a/doc/classes/Mutex.xml +++ b/doc/classes/Mutex.xml @@ -10,6 +10,8 @@ [b]Warning:[/b] To ensure proper cleanup without crashes or deadlocks, the following conditions must be met: - When a [Mutex]'s reference count reaches zero and it is therefore destroyed, no threads (including the one on which the destruction will happen) must have it locked. - When a [Thread]'s reference count reaches zero and it is therefore destroyed, it must not have any mutex locked. + + [b]Note:[/b] You can now use `add_user_signal` on mutexes to create thread-safe signals on them. $DOCS_URL/tutorials/performance/using_multiple_threads.html