PDA

View Full Version : Deleting the signal sender from a slot



Tulon
27th November 2016, 16:01
I found by accident that my code deletes a QObject while it's emitting a signal. The deletion happens from a slot connected to that signal. My understanding has always been that such a thing is not allowed. Yet, I've seen no crashes or warning messages in this case. Am I wrong assuming such a thing is not allowed or is it just luck it's not crashing?

A am using the latest Qt 5.7.

jimbo
27th November 2016, 20:16
Hello,

Does this help?
http://www.qtcentre.org/threads/67056-C-Qt5-Signals-with-no-receiver-what-happens

Regards

Tulon
27th November 2016, 21:20
Does this help?
http://www.qtcentre.org/threads/6705...r-what-happens

No, that thread discusses an unrelated topic.

d_stranz
28th November 2016, 00:17
is it just luck it's not crashing?

I'm guessing that you're just lucky. A signal to slot call is basically nothing more than an ordinary function call once the event loop invokes it. So if the last thing in the function is to emit the signal and the slot is the last one to be called, then the return from the signal is to code executing for an invalid instance, but if the stack unwinds properly and that instance is never used again, then you may have just gotten lucky.

On the other hand, if there were more slots to be serviced after the one that deleted the instance, or the return from the emit call executed more code for that instance, then it probably would have crashed. Deleting a QObject is -supposed- to disconnect any slots connected to its signals but I am not sure if it also cleans up any pending slot invocations resulting from the signal. Disconnecting slots prevents future invocations.

If you must delete a QObject-based class in a slot invoked by that instance, use deleteLater(). This allows the stack and event loop to clean up properly, and the instance will be deleted next time through the event loop.

Tulon
28th November 2016, 11:03
d_stranz, what you say makes perfect sense. If there was code following the emit statement that accesses the signalling object's state, it would certainly crash. Not my case though. As for having more slots connected, to be invoked following the deletion, I did such an experiment and those other slots simply don't get called. So, automatic disconnection does work in such a case.

My line of thinking was like this:
It's pretty simple for Qt developers to detect an object being deleted while it's emitting a signal and print a warning. Given that they don't, maybe it's not forbidden? I remember deleting a QObject from its event handler used to generate a warning message. It no longer does, presumably because it's now allowed. Unfortunately, the documentation is not explicit about either case.

Using deleteLater() is an option, although I would rather avoid it in my case, as the code performing object destruction works with an abstract interface and doesn't know it's dealing with a QObject.

d_stranz
28th November 2016, 18:03
I did such an experiment and those other slots simply don't get called. So, automatic disconnection does work in such a case.

Good to know. To be clear, you did something like this (with the connections in this order):

connect signal to slot 1, which deletes the instance
connect signal to slot 2, which uses the same instance
...
then called emit signal in some class method of the object instance.

And you observed that slot 1 is called, but the remaining ones were not.

Correct? This implies that the connections were both deleted and the calls removed from the event queue, which is what you hope would happen.


Using deleteLater() is an option, although I would rather avoid it in my case, as the code performing object destruction works with an abstract interface and doesn't know it's dealing with a QObject.

Nothing wrong with adding a deleteLater() method to your abstract interface, with the default calling operator delete() and calling deleteLater() for QObject-based concrete classes.

QObject instances emit a destroyed() signal immediately prior to destruction, so that's a way for Qt (and your own) code to clean up instance references.

Tulon
29th November 2016, 02:22
Good to know. To be clear, you did something like this (with the connections in this order):

connect signal to slot 1, which deletes the instance
connect signal to slot 2, which uses the same instance
...
then called emit signal in some class method of the object instance.

And you observed that slot 1 is called, but the remaining ones were not.

Correct? This implies that the connections were both deleted and the calls removed from the event queue, which is what you hope would happen.

Correct. Just to clarify, it's the emitter not the receiver that gets destroyed in slot 1. In slot 2 I only had a qDebug() and nothing else.



Nothing wrong with adding a deleteLater() method to your abstract interface, with the default calling operator delete() and calling deleteLater() for QObject-based concrete classes.

That's certainly an option.