Here is how Qt signal/slot connection works:
- Direct Connection The slot is invoked immediately, when the signal is emitted. The slot is executed in the emitter's thread, which is not necessarily the receiver's thread.
- Queued Connection The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
- Blocking Queued Connection The slot is invoked as for the Queued Connection, except the current thread blocks until the slot returns. Note: Using this type to connect objects in the same thread will cause deadlock.
And there's an extra one which is actually the default: Auto Connection If the signal is emitted in the thread which the receiving object has affinity then the behavior is the same as the Direct Connection. Otherwise, the behavior is the same as the Queued Connection."
The default works pretty well as expected in most cases:
- If within a worker thread, queued execution through a queued connection
- If within the object thread, execution is made right away
However, when a signal is emited from a thread, you have two options to handle it: "Queued" or "Blocking Queued" Connection.
Why is there no mode that would behave like that:
- If within a worker thread, blocking queued execution
- If within the object thread, execution is made right away
Because, as the documentation mentions, using a Blocking Queued Connection in the same thread will cause deadlock....so it's a real pain to handle, I often had to create and manage two signals and connections in my code to handle that:
class A
{
Q_OBJECT
public:
A()
{
connect( this, SIGNAL(changedFromThread()), this, SLOT(update()), Qt::BlockingQueuedConnection );
connect( this, SIGNAL(changedNotFromThread()), this, SLOT(update()) );
}
void notifySomethingChanged()
{
if ( QObject().thread() != thread() )
emit changedFromThread(); // would dead-lock if in same thread
else
emit changedNotFromThread();
}
public slots:
void update()
{
// Do some changes to A that cannot be done from a worker thread
}
signals:
void changedFromThread();
void changedNotFromThread();
};
If such a mode (let's call it Qt::AutoBlockingConnection
was available), I could have written:
class A
{
Q_OBJECT
public:
A()
{
connect( this, SIGNAL(changedFromThread()), this, SLOT(update()), Qt::AutoBlockingConnection );
}
void notifySomethingChanged()
{
emit changedFromThread(); // would dead-lock if in same thread
}
public slots:
void update()
{
// Do some changes to A that cannot be done from a worker thread
}
signals:
void changedFromThread();
};
Is there any good reason why a thread-friendly connection was only provided to swap between Qt::DirectConnection
and Qt::QueuedConnection
but none to swap between Qt::DirectConnection
and Qt::BlockingQueuedConnection
?