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).