Assume I have a Qt::QueuedConnection connection in which the signal is emitted in a different thread from the one the consumer slot is called. Now I want to close the application and in the handler destructor I would like to check either there are any remaining queued signal-slot calls (I am using this signal slot connection mechanism as a queue). Is it possible?
Related
I've come across the problem, that I might be sending signals from one QThread to another, however I have no way of checking if the thread that is implementing the slot is running. How will the framework handle such a situation while using Qt::QueuedConnection ?
...
WorkerImp* pWorker = new WorkerImp();
QThread worker;
pWorker->moveToThread(&worker);
QObject::connect(&worker, QThread::finished, pWorker, &QObject::deleteLater, Qt::QueuedConnection);
bool connected = QObject::connect(this, &SlaveMaster::requireWork, pWorker, &WorkerImp::doWork, Qt::QueuedConnection);
assert(connected);
// at this point we have connected the signal, thread is not starded.
// however the object that we use to connect still exists and will after we exit the thread.
worker.start();
worker.exit();
worker.wait();
// note that when we exit the QThread we do not destroy the object - it can be start over
emit requireWork();
...
The signal is never handled. A queued connection across threads is posted as an event to the thread, handled by its event loop. If the thread is stopped (and thus its event loop), there is no one to pick up the event and deliver it:
From the Qt Docs Signals and Slots Across Threads:
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.
and Per-Thread Event Loop:
[...] If no event loop is running, events won't be delivered to the object.
Note that a blocking queued connection would dead-lock
I have a Qt C++ program. I have a main driver MainWindow and a TCPClient class. The TCPClient class is used to communicate with a remote server, transmit some data over TCP, request for processing of the data and receive processed data from server. In my TCPClient class, I am using QAbstractSocket signal disconnected. This is emitted when the connection with the server is disconnected. In the function (slot) which handles this disconnect signal (ifDisconnected), onCompletionCallback function of the MainWindow is called. Now my question is how do I prevent the transmission of execution back to TCPClient after the said onCompletionCallback finishes executing. What's following is incomplete code describing the issue;
mainwindow.cpp
void MainWindow::on_connectButton_clicked()
{
std::function<void(void)> callback std::bind(&MainWindow::onCompletetionCallback, this);
tcpClient_ = new TCPClient(callback)->connectToServer(someData);
}
void MainWindow::onCompletetionCallback()
{
if(tcpClient_->isRequestSuccess())
{
QJsonDocument responseJson = tcpClient_->getResponse();
return; //When this finishes executing, I want to prevent the execution control to go back to TCPClient
}
}
TCPClient.cpp
void TCPClient::connectToServer(QJsonDocument requestJson)
{
// Removed code of other connect signals
connect(tcpSocket_, &QTcpSocket::disconnected, this, &TCPClient::ifDisconnected);
}
void TCPClient::ifDisconnected()
{
// Here the callback is called. After the callback finishes executing, I don't want execution to return to `TCPClient`.
onCompletionCallback_();
return;
}
How do I solve this problem. I need to use the signal disconnected because QAbstractSocket doesn't provide any utility function to check if the connection is available.
You cannot and you should not prevent that the signal handler returns to caller. Otherwise, you would corrupt your call stack.
The actual question (for me) is: What is the caller of signal handler?
To understand what I mean, please, read the Qt doc. about QObject::connect() with special attention to Qt::ConnectionType.
The default is Qt::AutoConnection which means:
If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Qt::DirectConnection:
The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
The most common case (for me) are signal handlers called for modifications of GUI objects (i.e. widgets, etc.) which modify data or other widgets in response (in strict single-threading manner). In this case, it is Qt::DirectConnection i.e. the widget signal emitter is the caller of my signal handler.
A possible error (I did once) is to delete the widget which emitted the signal (e.g. handling a close button event of a dialog) – bad idea: I destroyed the widget with a pending method call on call stack. After returning from my signal handler it ended in a crash. The caller method (the signal emitter) had no instance anymore, or in other words: its this was invalidated. (It's like sawing the limb you sit on.) (Btw. deleteLater could be one solution for this. I found SO: How delete and deleteLater works with regards to signals and slots in Qt? concerning this.)
Considering your code sample
connect(tcpSocket_, &QTcpSocket::disconnected, this, &TCPClient::ifDisconnected);
I suspect this is a Qt::DirectConnection.
The other aspect: calling a main window function out of the TCP client thread is something which needs special attention as well. The caller is something in the TCP client thread but addresses an object (the main window) which resides in the (different) GUI thread. Phew. Everything (exept local variables) what is accessed in this called function must be mutex guarded if the GUI thread itself uses this as well.
So, what about the other options:
Qt::QueuedConnection:
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.
For communication between threads, IMHO, the Qt::QueuedConnection is the safer way: The TCP client emits a signal which results in a respective entry in the event loop of the GUI thread (assuming the main window was given as receiver object). The GUI thread will pick up this entry while processing its event loop. In this case, the event loop of GUI thread is the caller of the signal handler. The TCP client thread didn't wait after sending the signal request but continued its processing. If this is not desired the third option comes into play:
Qt::BlockingQueuedConnection:
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
The Qt::BlockingQueuedConnection lets the signal emitter (the TCP client) until the GUI thread has processed the signal handler. (The warning about the dead-lock is not effective here as the TCP client thread is the signaling thread where the GUI thread is the receiver.)
I'm a little bit uncertain what to recommend. I'm afraid your application needs a little bit re-design but for this the code sample is a little bit to incomplete.
A possible solution:
Introduce a Qt signal emitted when completion is required. The MainWindow::onCompletetionCallback() may be connected as signal handler to this TCP client signal using a Qt::BlockingQueuedConnection.
If the end of transmission is recognized it may perhaps destroy the TCP client thread. However, a thread which kills another thread is no good idea in general (and I'm not sure how Qt handles this "under the hood"). Therefore, a better concept would be: If the end of transmission is recognized the main thread flags the TCP client thread to leave it's main loop. Flagging could be done e.g. with a std::atomic<bool> (or you stay in Qt which has its own pendant QAtomicInt. The TCP client checks this flag in its main loop or at least after emitting a signal and exits in case.
A last hint:
If you are uncertain whether you understood the all the signal stuff correctly – I checked my understanding by putting a break point into the signal handler and inspecting the call stack when execution stopped at that break point. This is easy and straight forward (except you are dealing with mouse or drag & drop events).
I'm asking in a case where there is a lot of qt events queued in event engine. Does QTimer emit timeout() signal through event and will with queuedConnection to a slot create another event?
The slots connected with Qt::DirectConnection will be called immediately when the QTimer::timeout signal is emitted by QTimer. If you use a queued connection, it will schedule an event to call the slot, which will be processed by the event loop. QTimer uses timerEvent to emit the timeout signal.
No it is not queued connection by default unless it is running in a different thread. Therefore it is DirectConnection.
I would say that, in general, if you want to guarantee a sequential order of events you are better of using Qt::QueuedConnection with things like timers even when in the same thread so that the event goes onto the queue and is not just acted upon immidiatley - since this behaviour is some-what more like an "interrupt".
However if you really want the timer timeout() to be acted on immediately then use Qt::DirectConnection - I feel that you need to be more careful with this connection type in general (with things like timers which can trigger in the middle of other normal code).
Edit
By "normal code" I mean...well, see comments below:
When a QTimer times out, every Signal-Slot connection, which joins signal of this QTimer with an slot, fires this slot exactly one time.
I was thinking about writing this code:
emit finished();
deleteLater();
But it made me wonder if finished() would always be delivered before the object is deleted. I'm pretty certain it will be delivered for Qt::DirectConnection, but I'm not sure about Qt::QueuedConnection, or if the slot is in another thread.
When you emit a signal, it is placed in the event queue of the thread of the receiving object for processing. Calling deleteLater also adds an event to the queue, so they will be performed sequentially if the receiving object and the object being deleted have the same thread affinity, regardless of the thread type.
If the sender and receiver have different thread affinity (running in different threads) then I would expect it is possible for deleteLater to be called before the emit is finished, if the receiver's event loop starts processing before the sender's event loop.
If you want to guarantee that finished is executed first, you can connect the sender and receiver with a blocking connection, which will halt the sender's thread until the message has been delivered.
connect(sender, SIGNAL(finished()), receiver, SLOT(handleFinished(), Qt::BlockingQueuedConnection);
Note that if using Qt::BlockingQueuedConnection and the sender and receiver have the same thread affinity, the application will be dead-locked.
I have done some research on this topic . the thread at SO also caught my interest and I wanted to summarize my understanding and be corrected if I am going wrong on a certain path and also wanted to know how QueuedConnection would work.
Here is my understanding followed by the question.
Signals can be connected manually to slots primarily through two different ways first way is using direct connection and the second way is queued connection. In case of a direct connection if the slot method that is attached to the signal is in the same thread then the slot method is called sequentially (as if it was just another method) however incase the slot is in a different thread from where the signal is launched then QueuedConnection would launch it when it finds it appropriate. (Now In this case I am not sure if it would launch a new thread or how it would proceed on doing that)
Slots don't belong to any particular thread, as they are just plain functions. But objects do. If you connect a signal to a slot by QueuedConnection, the signal emission will create an event and send that into the event queue of the target. Qt will arrange that your slot will be called when internally processing that event.
As for all events, they are processed in the thread of the object's thread affinity. You can change that thread by calling moveToThread on the target object.
In multithread environement when sender and recievr object is in diffrent thread.
Qt::QueuedConnection
What is happening when emiting thread? it simply emits( internally
postevent, to reciever threads message queue) and resume emiter
thread( not blocking).
what is happening on reciever thread, after executing above
statement? The slot is invoked when control returns to the event loop
of the receiver's thread.
Qt::BlockingQueuedConnection
What is happening on emiting thread ? it emits( internally sendEvent,
to reciever message queue) and block emiter thread until receiver
slot returnes.(Blocking).
what happening on receiever thread ? The slot is invoked when control
returns to the event loop of the receiver's thread.
Roughly speaking, for QueuedConnection Qt will make a queue of received signals for the thread the slot belongs to, and will launch them one by one in order they are stored in the queue when the thread becomes available (it finishes whatever it was doing and returns to event loop).
No new thread will be launched - slot belongs to some thread, so execution will be performed in that thread.