Some questions about local event loops
-
Hi everyone, I've tried to study everything possible about event loops but I still have some doubts left. I hope I'm not asking silly questions.
I am developing some new features (client/server sockets) on application where I absolutely cannot use the main event loop. I know that I can use a local event loop, which I have already tried to do, but I have some questions left that I expose to you:
-
Does it make sense to use a "custom" event loop shared between the various classes (let me say "parallel" to the main loop)? Or is it better that each class has its own?
-
Does it make sense to declare a QEventLoop as a member of a class, or is it better an instance at member level as I see in some examples?
-
In some examples I have seen has this pattern: QEventLoop instance => connect... => exec(). Is connect able to understand that there is a local event loop and use it? Or is there need to change something to use the local one?
-
In my code some "connect" take place in the slot, upon receipt of the event. Is it possible to instantiate a local event loop in inside the slop or is it not recommended / harmful? if not, what is an alternative?
Thanks to everyone who can help me.
-
-
hi @Merlino said in Some questions about local event loops:
where I absolutely cannot use the main event loop
what does that mean? You do not have a Q(Core)Application instance ?
-
@J-Hilk is a very large application developed some time ago by another programmer. For some reason that I haven't had time to investigate, the main loop isn't running and therefore doesn't allow event handling. I want to avoid intervening on parts of code that are not part of my development, both to avoid creating regressions and due to lack of time.
-
@Merlino to be honest, I would stay away from Local Event loops, especially since you do not know what is going on with the main event loop of the actual application.
Two Options I would consider
1:
Treating your additional part as its own thing. move that to it's own thread. a QThread has its own event loop and you do not have to micro manage2:
The case, where you do not have an event loop is the reason why the synchronous APIs of Qt Classes exist at all. QSocket should have an alternative sets of synchronous functions that you could use.PS: I can't believe that I actually suggested synchronous apis of Qt 2021 is shaping up to be an exceptional year
-
@Merlino
One separate thing to be aware of. If it's really true that, the main loop isn't running and therefore doesn't allow event handling
be aware that, according to my understanding, this means
QObject
s which are deleted viadeleteLater()
will not get their memory/resources freed. A localQEventLoop
apparently does not allow that to happen. So keep an eye on any memory usage. -
@Merlino said in Some questions about local event loops:
I hope I'm not asking silly questions.
There is no such thing as a silly question when you're learning :-)
I'll answer your questions in a slightly different order:
- In some examples I have seen has this pattern: QEventLoop instance => connect... => exec(). Is connect able to understand that there is a local event loop and use it? Or is there need to change something to use the local one?
connect()
does not know or care about which event loop(s) are available.The only thing that matters is this: When a (queued) slot needs to run, the currently-active event loop will run the slot. This will happen even if the loop did not exist when the connection was made.
-
Does it make sense to use a "custom" event loop shared between the various classes (let me say "parallel" to the main loop)? Or is it better that each class has its own?
-
Does it make sense to declare a QEventLoop as a member of a class, or is it better an instance at member level as I see in some examples?
Note 1: You cannot call
exec()
on a QEventLoop that is already running. However, you can stop it and thenexec()
it again afterwards.Note 2: Each thread (including the main thread) can only have 1 active event loop at a time. When you start a new event loop, you suspend the original loop for the duration of your new loop. In other words, your new loop is nested inside the original loop (as opposed to being in parallel with the original loop). For example, when you create a dialog and call
QDialog::exec()
, that runs a new loop that blocks the main event loop until you dismiss the dialog.I can't say I've seen designs where a QEventLoop is a static member of a class or shared between objects. My main question is: How many loops do you need?
- In my code some "connect" take place in the slot, upon receipt of the event. Is it possible to instantiate a local event loop in inside the slop or is it not recommended / harmful? if not, what is an alternative?
It is possible, but are you sure you need this? If your slot is running, that means an event loop must already be running.
I want to avoid intervening on parts of code that are not part of my development, both to avoid creating regressions and due to lack of time.
Be aware that this approach can cause technical debt to accumulate rapidly, so please take steps to minimize it.
-
@Merlino said in Some questions about local event loops:
For some reason that I haven't had time to investigate, the main loop isn't running and therefore doesn't allow event handling. I want to avoid intervening on parts of code that are not part of my development, both to avoid creating regressions and due to lack of time.
I know, maintaining an old software which you haven't developed yourself is a paint!
But, without working main event loop, QObject did not work well.
You may have many obscure/hidden issues like memory never released, because functions likeQObject::deleteLater()
delaying there execution and are done on next event loop processing.My suggestion would be to create a dedicated QThread and move you "old code" execution to this thread and start your "new code" in the main thread.
I don't have any knowledge about your software, so maybe this is not possible for you.The first thing you have to do, to take complet power of Qt framework, is to ensure thread event loop never goes locked for a too long time.
Qt is an asynchronous framework, and the event loop is a very important part to ensure all Qt features (like signals/slots) are working well. -
I don't think that you have any real chance to get it working with Qt sockets with the restrictions you want to have.
There are several things to consider. If you run an event queue inside any thread (might also just be the main thread) it will block at that point. Thus, having any event loop for objects located inside the main thread will block the existing application. This means that you need two separate threads: one handling the old application code and one handling your new socket code. I am not entirely sure if you can place the Qt socket code into a separate thread. My best guess (though I don't know for sure) is that the OS will somehow inform the main thread of your application about network events. If you don't have a Qt event loop running in the main thread these will not be translated into Qt signals and nothing will be triggered inside the event loop of your separate thread. This leaves you with @KroMignon 's suggestion to have your new code inside the main thread running an event loop and moving the old code to a separate thread. BTW, you could (for some sort of simplicity) use C++11 to create a new thread if you don't want to put too much of Qt into your existing application.
If you don't have any Qt in your application yet I would suggest having a look into asio (https://think-async.com/Asio/). You can get it as part of Boost or separately. It works asynchronously, but does not have an event loop.
-
Thank you all for the valuable information you have given me!
In the end I got around the inability to use the main event loop by running my new part on a separate thread, with this pattern:
QThread myThread;
MyNewPart newPart;
newPart.moveToThread(&myThread);
/* Connect(...) QThread::started -> MyNewPart::run*/
myThread.start();Everything works without changing the code :)