I am not thinking of the problem that queued signals are still delivered in the receiving thread after disconnect()
, instead:
Consider the case that a Sender
object is generating signals in thread 1 and there is a Receiver
object in thread 2 which has a slot that is connected to Sender
's signal via a Qt::DirectConnection
.
Now, in Receiver::~Receiver()
, I need to make sure that no signals are still delivered while the object is already (maybe partially) destructed. Because the connection is direct, the slot could be invoked in thread 1 at any time and could in particular happen between destruction of Receiver
's specific attributes and destruction of the base QObject
which will also disconnect signals. So, my question boils down to:
Is it enough to disconnect Sender
and Receiver
objects in thread 2 before destruction of the Receiver
object, or do I need to make sure that no signals are emitted in thread 1 during the disconnect()
call?
I am thinking of the case where thread 1 is in the middle of emitting the signal, e.g. right at the top of executing the receiving slot and right at that moment, in thread 2, the disconnect()
call is done. If disconnect()
waits (via a mutex) for thread 1 to finish delivering the signal before doing the disconnect and blocking further signal deliveries, everything would be fine but I am not sure that's the case.
Yes disconnect()
and connect()
are using mutexes for protection.
These are the first lines of the disconnect()
function:
bool QMetaObjectPrivate::disconnect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index,
DisconnectType disconnectType)
{
if (!sender)
return false;
QObject *s = const_cast<QObject *>(sender);
QMutex *senderMutex = signalSlotLock(sender);
QMutex *receiverMutex = receiver ? signalSlotLock(receiver) : 0;
QOrderedMutexLocker locker(senderMutex, receiverMutex);
And here are the first lines of the connect()
function:
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver, int method_index, int type, int *types)
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
If you check the QObject
documentation you can see that :
Note: All functions in this class are reentrant, but connect(), connect(), disconnect(), and disconnect() are also thread-safe.
EDIT
When a signal is emitted the QMetaObject::activate function is called which locks the sender's object mutex:
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
...
QMutexLocker locker(signalSlotLock(sender));