Make sure we know when deleting an emitting object

We used a lock signals in the signal_map while emitting, because it was
not allowed to disconnect them while being emitted.
We used that lock to check if we where deleting an object during signal
emission.
Now that we allow to disconnect signals while they are being emitted, if
an object first disconnects, then gets deleted we can't know that a
signal was being emitted during the destructor.

This commit adds a new `_emitting` boolean member to Object to be set
while emitting and checked in the destructor, while removing the old
signal lock which is now unused.
This commit is contained in:
Fabio Alessandrelli 2020-01-22 01:45:06 +01:00
parent 5127afa812
commit 41f59ecfca
2 changed files with 10 additions and 9 deletions

View File

@ -1213,9 +1213,9 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
MessageQueue::get_singleton()->push_call(target->get_instance_id(), c.method, args, argc, true);
} else {
Variant::CallError ce;
s->lock++;
_emitting = true;
target->call(c.method, args, argc, ce);
s->lock--;
_emitting = false;
if (ce.error != Variant::CallError::CALL_OK) {
#ifdef DEBUG_ENABLED
@ -1920,6 +1920,7 @@ Object::Object() {
_instance_id = ObjectDB::add_instance(this);
_can_translate = true;
_is_queued_for_deletion = false;
_emitting = false;
instance_binding_count = 0;
memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
script_instance = NULL;
@ -1942,15 +1943,15 @@ Object::~Object() {
const StringName *S = NULL;
if (_emitting) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
ERR_PRINTS("Object " + to_string() + " 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.");
}
while ((S = signal_map.next(NULL))) {
Signal *s = &signal_map[*S];
if (s->lock > 0) {
//@todo this may need to actually reach the debugger prioritarily somehow because it may crash before
ERR_PRINTS("Object was freed or unreferenced while signal '" + String(*S) + "' 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.");
}
//brute force disconnect for performance
int slot_count = s->slot_map.size();
const VMap<Signal::Target, Signal::Slot>::Pair *slot_list = s->slot_map.get_array();

View File

@ -465,8 +465,7 @@ private:
MethodInfo user;
VMap<Target, Slot> slot_map;
int lock;
Signal() { lock = 0; }
Signal() {}
};
HashMap<StringName, Signal> signal_map;
@ -481,6 +480,7 @@ private:
bool _predelete();
void _postinitialize();
bool _can_translate;
bool _emitting;
#ifdef TOOLS_ENABLED
bool _edited;
uint32_t _edited_version;