@@ -65,6 +65,23 @@ struct _ObjectDebugLock {
6565
6666#endif
6767
68+ struct _ObjectSignalLock {
69+ Mutex *mutex;
70+ _ObjectSignalLock (const Object *const p_obj) {
71+ mutex = p_obj->signal_mutex ;
72+ if (mutex) {
73+ mutex->lock ();
74+ }
75+ }
76+ ~_ObjectSignalLock () {
77+ if (mutex) {
78+ mutex->unlock ();
79+ }
80+ }
81+ };
82+
83+ #define OBJ_SIGNAL_LOCK _ObjectSignalLock _signal_lock (this );
84+
6885PropertyInfo::operator Dictionary () const {
6986 Dictionary d;
7087 d[" name" ] = name;
@@ -255,6 +272,9 @@ void Object::_initialize() {
255272}
256273
257274void Object::_postinitialize () {
275+ if (_uses_signal_mutex ()) {
276+ signal_mutex = memnew (Mutex);
277+ }
258278 notification (NOTIFICATION_POSTINITIALIZE);
259279}
260280
@@ -1123,20 +1143,27 @@ void Object::get_meta_list(List<StringName> *p_list) const {
11231143void Object::add_user_signal (const MethodInfo &p_signal) {
11241144 ERR_FAIL_COND_MSG (p_signal.name .is_empty (), " Signal name cannot be empty." );
11251145 ERR_FAIL_COND_MSG (ClassDB::has_signal (get_class_name (), p_signal.name ), vformat (" User signal's name conflicts with a built-in signal of '%s'." , get_class_name ()));
1146+
1147+ OBJ_SIGNAL_LOCK
1148+
11261149 ERR_FAIL_COND_MSG (signal_map.has (p_signal.name ), vformat (" Trying to add already existing signal '%s'." , p_signal.name ));
11271150 SignalData s;
11281151 s.user = p_signal;
11291152 signal_map[p_signal.name ] = s;
11301153}
11311154
11321155bool Object::_has_user_signal (const StringName &p_name) const {
1156+ OBJ_SIGNAL_LOCK
1157+
11331158 if (!signal_map.has (p_name)) {
11341159 return false ;
11351160 }
11361161 return signal_map[p_name].user .name .length () > 0 ;
11371162}
11381163
11391164void Object::_remove_user_signal (const StringName &p_name) {
1165+ OBJ_SIGNAL_LOCK
1166+
11401167 SignalData *s = signal_map.getptr (p_name);
11411168 ERR_FAIL_NULL_MSG (s, " Provided signal does not exist." );
11421169 ERR_FAIL_COND_MSG (!s->removable , " Signal is not removable (not added with add_user_signal)." );
@@ -1183,46 +1210,53 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
11831210 return ERR_CANT_ACQUIRE_RESOURCE; // no emit, signals blocked
11841211 }
11851212
1186- SignalData *s = signal_map.getptr (p_name);
1187- if (!s) {
1213+ Callable *slot_callables = nullptr ;
1214+ uint32_t *slot_flags = nullptr ;
1215+ uint32_t slot_count = 0 ;
1216+
1217+ {
1218+ OBJ_SIGNAL_LOCK
1219+
1220+ SignalData *s = signal_map.getptr (p_name);
1221+ if (!s) {
11881222#ifdef DEBUG_ENABLED
1189- bool signal_is_valid = ClassDB::has_signal (get_class_name (), p_name);
1190- // check in script
1191- ERR_FAIL_COND_V_MSG (!signal_is_valid && !script.is_null () && !Ref<Script>(script)->has_script_signal (p_name), ERR_UNAVAILABLE, vformat (" Can't emit non-existing signal \" %s\" ." , p_name));
1223+ bool signal_is_valid = ClassDB::has_signal (get_class_name (), p_name);
1224+ // check in script
1225+ ERR_FAIL_COND_V_MSG (!signal_is_valid && !script.is_null () && !Ref<Script>(script)->has_script_signal (p_name), ERR_UNAVAILABLE, vformat (" Can't emit non-existing signal \" %s\" ." , p_name));
11921226#endif
1193- // not connected? just return
1194- return ERR_UNAVAILABLE;
1195- }
1227+ // not connected? just return
1228+ return ERR_UNAVAILABLE;
1229+ }
11961230
1197- // If this is a ref-counted object, prevent it from being destroyed during signal emission,
1198- // which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889.
1199- Ref<RefCounted> rc = Ref<RefCounted>(Object::cast_to<RefCounted>(this ));
1231+ // If this is a ref-counted object, prevent it from being destroyed during signal emission,
1232+ // which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889.
1233+ Ref<RefCounted> rc = Ref<RefCounted>(Object::cast_to<RefCounted>(this ));
12001234
1201- // Ensure that disconnecting the signal or even deleting the object
1202- // will not affect the signal calling.
1203- Callable *slot_callables = (Callable *)alloca (sizeof (Callable) * s->slot_map .size ());
1204- uint32_t *slot_flags = (uint32_t *)alloca (sizeof (uint32_t ) * s->slot_map .size ());
1205- uint32_t slot_count = 0 ;
1235+ // Ensure that disconnecting the signal or even deleting the object
1236+ // will not affect the signal calling.
1237+ slot_callables = (Callable *)alloca (sizeof (Callable) * s->slot_map .size ());
1238+ slot_flags = (uint32_t *)alloca (sizeof (uint32_t ) * s->slot_map .size ());
12061239
1207- for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map ) {
1208- memnew_placement (&slot_callables[slot_count], Callable (slot_kv.value .conn .callable ));
1209- slot_flags[slot_count] = slot_kv.value .conn .flags ;
1210- ++slot_count;
1211- }
1240+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map ) {
1241+ memnew_placement (&slot_callables[slot_count], Callable (slot_kv.value .conn .callable ));
1242+ slot_flags[slot_count] = slot_kv.value .conn .flags ;
1243+ ++slot_count;
1244+ }
12121245
1213- DEV_ASSERT (slot_count == s->slot_map .size ());
1246+ DEV_ASSERT (slot_count == s->slot_map .size ());
12141247
1215- // Disconnect all one-shot connections before emitting to prevent recursion.
1216- for (uint32_t i = 0 ; i < slot_count; ++i) {
1217- bool disconnect = slot_flags[i] & CONNECT_ONE_SHOT;
1248+ // Disconnect all one-shot connections before emitting to prevent recursion.
1249+ for (uint32_t i = 0 ; i < slot_count; ++i) {
1250+ bool disconnect = slot_flags[i] & CONNECT_ONE_SHOT;
12181251#ifdef TOOLS_ENABLED
1219- if (disconnect && (slot_flags[i] & CONNECT_PERSIST) && Engine::get_singleton ()->is_editor_hint ()) {
1220- // This signal was connected from the editor, and is being edited. Just don't disconnect for now.
1221- disconnect = false ;
1222- }
1252+ if (disconnect && (slot_flags[i] & CONNECT_PERSIST) && Engine::get_singleton ()->is_editor_hint ()) {
1253+ // This signal was connected from the editor, and is being edited. Just don't disconnect for now.
1254+ disconnect = false ;
1255+ }
12231256#endif
1224- if (disconnect) {
1225- _disconnect (p_name, slot_callables[i]);
1257+ if (disconnect) {
1258+ _disconnect (p_name, slot_callables[i]);
1259+ }
12261260 }
12271261 }
12281262
@@ -1280,6 +1314,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
12801314 // without access to ADD_SIGNAL in bind_methods
12811315 // added events are per instance, as opposed to the other ones, which are global
12821316
1317+ OBJ_SIGNAL_LOCK
1318+
12831319 MethodInfo mi;
12841320 mi.name = p_name;
12851321
@@ -1360,6 +1396,8 @@ bool Object::has_signal(const StringName &p_name) const {
13601396}
13611397
13621398void Object::get_signal_list (List<MethodInfo> *p_signals) const {
1399+ OBJ_SIGNAL_LOCK
1400+
13631401 if (!script.is_null ()) {
13641402 Ref<Script> scr = script;
13651403 if (scr.is_valid ()) {
@@ -1379,6 +1417,8 @@ void Object::get_signal_list(List<MethodInfo> *p_signals) const {
13791417}
13801418
13811419void Object::get_all_signal_connections (List<Connection> *p_connections) const {
1420+ OBJ_SIGNAL_LOCK
1421+
13821422 for (const KeyValue<StringName, SignalData> &E : signal_map) {
13831423 const SignalData *s = &E.value ;
13841424
@@ -1389,6 +1429,8 @@ void Object::get_all_signal_connections(List<Connection> *p_connections) const {
13891429}
13901430
13911431void Object::get_signal_connection_list (const StringName &p_signal, List<Connection> *p_connections) const {
1432+ OBJ_SIGNAL_LOCK
1433+
13921434 const SignalData *s = signal_map.getptr (p_signal);
13931435 if (!s) {
13941436 return ; // nothing
@@ -1400,6 +1442,7 @@ void Object::get_signal_connection_list(const StringName &p_signal, List<Connect
14001442}
14011443
14021444int Object::get_persistent_signal_connection_count () const {
1445+ OBJ_SIGNAL_LOCK
14031446 int count = 0 ;
14041447
14051448 for (const KeyValue<StringName, SignalData> &E : signal_map) {
@@ -1416,13 +1459,16 @@ int Object::get_persistent_signal_connection_count() const {
14161459}
14171460
14181461void Object::get_signals_connected_to_this (List<Connection> *p_connections) const {
1462+ OBJ_SIGNAL_LOCK
1463+
14191464 for (const Connection &E : connections) {
14201465 p_connections->push_back (E);
14211466 }
14221467}
14231468
14241469Error Object::connect (const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
14251470 ERR_FAIL_COND_V_MSG (p_callable.is_null (), ERR_INVALID_PARAMETER, vformat (" Cannot connect to '%s': the provided callable is null." , p_signal));
1471+ OBJ_SIGNAL_LOCK
14261472
14271473 if (p_callable.is_standard ()) {
14281474 // FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
@@ -1491,6 +1537,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
14911537
14921538bool Object::is_connected (const StringName &p_signal, const Callable &p_callable) const {
14931539 ERR_FAIL_COND_V_MSG (p_callable.is_null (), false , vformat (" Cannot determine if connected to '%s': the provided callable is null." , p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
1540+ OBJ_SIGNAL_LOCK
1541+
14941542 const SignalData *s = signal_map.getptr (p_signal);
14951543 if (!s) {
14961544 bool signal_is_valid = ClassDB::has_signal (get_class_name (), p_signal);
@@ -1509,6 +1557,8 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable
15091557}
15101558
15111559bool Object::has_connections (const StringName &p_signal) const {
1560+ OBJ_SIGNAL_LOCK
1561+
15121562 const SignalData *s = signal_map.getptr (p_signal);
15131563 if (!s) {
15141564 bool signal_is_valid = ClassDB::has_signal (get_class_name (), p_signal);
@@ -1532,6 +1582,7 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
15321582
15331583bool Object::_disconnect (const StringName &p_signal, const Callable &p_callable, bool p_force) {
15341584 ERR_FAIL_COND_V_MSG (p_callable.is_null (), false , vformat (" Cannot disconnect from '%s': the provided callable is null." , p_signal)); // Should use `is_null`, see note in `connect` about the use of `is_valid`.
1585+ OBJ_SIGNAL_LOCK
15351586
15361587 SignalData *s = signal_map.getptr (p_signal);
15371588 if (!s) {
@@ -1569,6 +1620,10 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
15691620 return true ;
15701621}
15711622
1623+ bool Object::_uses_signal_mutex () const {
1624+ return true ;
1625+ }
1626+
15721627void Object::_set_bind (const StringName &p_set, const Variant &p_value) {
15731628 set (p_set, p_value);
15741629}
@@ -2204,33 +2259,36 @@ Object::~Object() {
22042259 ERR_PRINT (vformat (" Object '%s' was freed or unreferenced while a signal is being emitted from it. Try connecting to the signal using 'CONNECT_DEFERRED' flag, or use queue_free() to free the object (if this object is a Node) to avoid this error and potential crashes." , to_string ()));
22052260 }
22062261
2207- // Drop all connections to the signals of this object.
2208- while (signal_map.size ()) {
2209- // Avoid regular iteration so erasing is safe.
2210- KeyValue<StringName, SignalData> &E = *signal_map.begin ();
2211- SignalData *s = &E.value ;
2212-
2213- for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map ) {
2214- Object *target = slot_kv.value .conn .callable .get_object ();
2215- if (likely (target)) {
2216- target->connections .erase (slot_kv.value .cE );
2262+ {
2263+ OBJ_SIGNAL_LOCK
2264+ // Drop all connections to the signals of this object.
2265+ while (signal_map.size ()) {
2266+ // Avoid regular iteration so erasing is safe.
2267+ KeyValue<StringName, SignalData> &E = *signal_map.begin ();
2268+ SignalData *s = &E.value ;
2269+
2270+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map ) {
2271+ Object *target = slot_kv.value .conn .callable .get_object ();
2272+ if (likely (target)) {
2273+ target->connections .erase (slot_kv.value .cE );
2274+ }
22172275 }
2218- }
22192276
2220- signal_map.erase (E.key );
2221- }
2222-
2223- // Disconnect signals that connect to this object.
2224- while (connections.size ()) {
2225- Connection c = connections.front ()->get ();
2226- Object *obj = c.callable .get_object ();
2227- bool disconnected = false ;
2228- if (likely (obj)) {
2229- disconnected = c.signal .get_object ()->_disconnect (c.signal .get_name (), c.callable , true );
2277+ signal_map.erase (E.key );
22302278 }
2231- if (unlikely (!disconnected)) {
2232- // If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
2233- connections.pop_front ();
2279+
2280+ // Disconnect signals that connect to this object.
2281+ while (connections.size ()) {
2282+ Connection c = connections.front ()->get ();
2283+ Object *obj = c.callable .get_object ();
2284+ bool disconnected = false ;
2285+ if (likely (obj)) {
2286+ disconnected = c.signal .get_object ()->_disconnect (c.signal .get_name (), c.callable , true );
2287+ }
2288+ if (unlikely (!disconnected)) {
2289+ // If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
2290+ connections.pop_front ();
2291+ }
22342292 }
22352293 }
22362294
@@ -2248,6 +2306,10 @@ Object::~Object() {
22482306 }
22492307 memfree (_instance_bindings);
22502308 }
2309+
2310+ if (signal_mutex) {
2311+ memdelete (signal_mutex);
2312+ }
22512313}
22522314
22532315bool predelete_handler (Object *p_object) {
0 commit comments