How Qt Handle Events and Signal in Same EventLoop - qt

I couldn't understand how qt handle events (e.g timer event, socket event etc.) and signals in same event loop.As I understand,timer and socket events are handled via select system call(in Unix like OS).
How an event loop handle signals while sleeping because of select system call.

In Qt, signals are used to call slots. When you emit a signal, there are, roughly speaking, only 2 options for calling the corresponding slot:
Direct slot call. This can be thought of as replacing a line with a signal emitting by a line with just a slot call. An event loop is not used to process this signal itself.
Delayed slot call. In this case, the signal will be converted to an event, and the event will be posted to the receiver event loop (the event enqueues in the event loop of the thread the receiver object is living in). From now on, for the processing receiver event loop, it makes no difference whether it was a signal or an event. The event will be picked up by the event loop and will cause the slot invocation sometime later.
From Qt doc: https://doc.qt.io/qt-5/signalsandslots.html#signals
When a signal is emitted, the slots connected to it are usually
executed immediately, just like a normal function call. When this
happens, the signals and slots mechanism is totally independent of any
GUI event loop. Execution of the code following the emit statement
will occur once all slots have returned. The situation is slightly
different when using queued connections; in such a case, the code
following the emit keyword will continue immediately, and the slots
will be executed later.
As for understanding an event loop, an event loop is just a loop which process one event from an event queue on each iteration.
In short, this can be represented as follows:
QQueue<QEvent> eventQueue; // Events (and pending slot calls as well) are added to this queue
...
// How an event loop works (schematically):
while(event = eventQueue.dequeue())
{
do_what_the_event_wants_or_ignore_it(event);
}
Read also https://wiki.qt.io/Threads_Events_QObjects

Related

Cancel QThread in PyQt5

I have a GUI in PyQt5, which starts a QThread that reads from a serial port. The thread does quit, when it read all the data, but I want to be able to stop it when I click on a stop button. How do I do that? Here is the basic code:
# ...
class Worker(QObject):
finished = pyqtSignal()
progress = pyqtSignal(list)
def __init__(self):
QObject.__init__(self)
self._Reader = Reader()
self._Reader.progress = self.progress
self._Reader.finished = self.finished
def run(self):
self._Reader.read()
class Ui(QtWidgets.QMainWindow):
# ...
def startClicked(self):
self.thread = QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
self.worker.finished.connect(self.thread.quit)
self.worker.finished.connect(self.worker.deleteLater)
self.worker.finished.connect(self.workerFinished)
self.thread.finished.connect(self.thread.deleteLater)
self.worker.progress.connect(self.reportProgress)
self.thread.start()
def stopClicked(self):
# How do I stop the thread?
pass
when managing threads you can do, as states in the doc here: https://doc.qt.io/qt-5/qthread.html
You can stop the thread by calling exit() or quit().
https://doc.qt.io/qt-5/qthread.html#exit
exit:
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.
By convention, a returnCode of 0 means success, any non-zero value indicates an error.
https://doc.qt.io/qt-5/qthread.html#quit
quit:
Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).
This function does nothing if the thread does not have an event loop.
I assume that you read data in some data processing loop. If this assumption is wrong, then the following is not valid, of course.
You cannot call secondary thread's quit() directly from the main thread and expect that the secondary thread will process it immediately and quit the thread. The reason is that the thread is busy reading the data in the data processing loop. So you need to break the data processing loop in the secondary thread to make the event loop idle.
(Btw. do not confuse the data processing loop with the event loop. Data processing loop is the one which you have written yourself to read data from the port. The event loop is the loop created by Qt automatically after you called QThread::start() and which is processing events, signals and slots in the secondary thread. This event loop is blocked while your data processing loop is running.)
In order to break the data processing loop, you need to do two things:
call QThread::requestInterruption() from the main thread as response to some "Abort" button having been pressed (do not worry about thread safety, requesting interruption is thread safe/atomic)
within the loop in the secondary thread you need to periodically check QThread::isInterruptionRequested(), and if this returns true, then break the loop and emit worker's finished() signal
Once you broke from the data processing loop in the secondary thread, the event loop in the secondary thread becomes available for processing signals sent from the main thread.
I can see in your code that worker's finished() signal is connected to QThread::quit(). So emitting finished() from the secondary thread (after you broke from the data processing loop) will call thread's quit() which will be processed by the secondary thread's event loop (which is now idle) and it will quit the event loop and subsequently the thread and if you have connected everything correctly it will delete the worker and the thread. (though I have not checked this part of your code)

How to make the slot associate with QTimer::timeout can be interrupted by any other signal processor(higher priority) and then resume?

To simplify the question, let's say that i have a QTimer, which will trigger its timeout event every 3000ms.
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [&](){
// do sth(rely on a public data structure)
});
timer.start(3000);
The operation inside the lambda connected to the timeout event is relied on a public data structure.
And the application hold an QUdpSocket, and connect the readyRead signal with a slot function.
QUdpSocket socket;
socket.bind(45454, QUdpSocket::ReuseAddressHint);
QObject::connect(&socket, &QUdpSocket::readyRead, [&](){
//manipulate the public data structure
}
As you can see, the lambda connected to the readyRead signal manipulate the public data structure that the first lambda rely on.
So my question is, i want the function that connectted to the readyRead signal has the highest "priority", that is, even in the Qt's event loop are dealing with the timeout slot now, it can be interrupted and start the readyRead slot immediately, then after it finish, resume the timeout slot function. Is there any way to do so?
(My homework is to simulate the IEEE802.11 exposed/hidden node problem, it requires that i have to constantly listen the channel before/during sending a packet.)
(Is explicitly call QCoreApplication::processEvent will help?)
I am not familiar with the IEEE802.11 exposed/hidden node problem, however i don't think you can "interrupt" your code as you're describing.
One possible way to handle with this would be running the code for the readyRead and the timeout slots on different threads and then use some kind of synchronisation mechanism (QMutex comes to mind) to access the public data (and get its state).
i.e.
add some kind of uniqueid to identify the public_data current status
timeout_slot will acquire a lock, read the public data in a local copy and release the lock, then continue to manipulate local structure and finally before release would acquire lock again and check that the uniqueid has not been changed, if so commit your work, otherwise you'd have to start over.
readyRead_slot would acquire the lock, update the uniqueid and then continue working

Direct connection and event loop [duplicate]

This question already has answers here:
Qt signals (QueuedConnection and DirectConnection)
(3 answers)
Closed 8 years ago.
I have read the doc of Qt and have the following question:
if I have:
class_A::on_button_click(){
...do some things...//no signal emit this part
emit signal_A();
}
class_B::on_signal_received(){
...do some things...//no signal emit this part
}
...
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received);
All the things here are in the main thread,
now when I click the button and trigger the first function,
the program will be executed from the first line of
class_A::on_button_click()
until the last line of
class_B::on_signal_received()
and during the process nothing else in the main thread will get the chance to be executed, is this correct?( "...do some things..." part don't emit any signals)
In other words, when is the moment that the control return to the event loop?
After finishing
class_A::on_button_click()
or
class_B::on_signal_received()
?
When your signal and slot are in the same thread (as mentioned by user3183610) your signal will be direct connection (the default for same-thread). This effectively runs similarly to a function call. The signal is not queued, but instead the slot that the signal is connected to executes immediately - so you can think of it as a direct function call to the slot.
You can, however, change this behavior by using the Qt::QueuedConnection optional parameter at the end of your connect call:
connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received, Qt::QueuedConnection);
This will force the use of the queue, your signal will be queued in the event loop and then other pending signals will be executed in order (this is often more desirable then DirectConnection because you can more easily guarantee the order of events). I tend towards to use of queued connections for this reason (though I believe direct is very slightly more efficient).
So for your code there is no return to the event loop until after on_button_click(). During on_button_click() you emit the diret signal signal_x() and immediately on_signal_received() is called (by-passing the event loop), when this finishes it returns back to on_button_click() - just like a function call would :)

Are events of a general event processor processed in parallel

I currently have attached a signal to a slot of type QueuedConnection and I know with this type the signal is processed by the event handler of the destination object which holds the slot.Now my question is are the events in a general event handler processed in parallel ?
No, they are processed sequentially

Signal handling

The case is: I register a signal handler with signal function.
The question:
Is handler always called independently of the process state? (I mean its stopped, etc.). What happens there?(dependently of the state)
Are handler functions registered as some "special functions" by system (i.e. when handeler runs other signals are not recieved and are put into the stack or smth like that. Or maybe they are simply ignored.) If not, imagine that when handler strats, process gets another signal, then this handler is called again inspite of "the first" hasn't completed its task yet and so on.
"While a process is stopped, any additional signals that are sent to the process shall not be delivered until the process is continued, except SIGKILL (...) The default action for SIGCONT is to resume execution at the point where the process was stopped, after first handling any pending unblocked signals." (Unix standard, section Signal Concepts.)
Ordinarily, nothing special happens. When a signal X is caught while in a signal handler for Y, execution is simply transferred to the handler for X, after which the handler for Y resumes execution.
The following program demonstrates this behavior. raise(sig) sends a signal to the calling process (it's like kill(getpid(), sig)).
void hello(int unused)
{
printf("Hello, ");
raise(SIGUSR2);
printf("!\n");
}
void world(int unused)
{
printf("world");
}
int main()
{
signal(SIGUSR1, hello);
signal(SIGUSR2, world);
raise(SIGUSR1);
return 0;
}
This is "safe" because a process will accept signals only from processes with the same user ID (or root), so you can only shoot yourself in the foot this way.

Resources