Discussion:
[Interest] Relationship between a QEventLoop and
Jorge Fierro
2016-08-03 17:22:36 UTC
Permalink
Hi. I've been reading through the documentation and mailing list
archives and I haven't found an authoritative and/or conclusive answer to
the following question:

What exactly is the relationship between an event loop entered by
calling QEventLoop::exec() and *the* main event loop (the one running
when you can QCoreApplication::exec())?

There are a number of related questions:

- Does QEventLoop::exec() enter *the* main event loop? Or,
- Does QEventLoop::exec() enter a different event loop independent
from QCoreApplication::exec()?
- Can QEventLoop::exec() be called outside QCoreApplication::exec()
(i.e., in a call stack that didn't originate from
QCoreApplication::exec())?
- If QEventLoop::exec() spins the main loop then any event at all can
be generated. Is this why one must use it very carefully to avoid
reentrancy issues?

Thanks.
Thiago Macieira
2016-08-03 17:58:06 UTC
Permalink
Post by Jorge Fierro
What exactly is the relationship between an event loop entered by
calling QEventLoop::exec() and *the* main event loop (the one running
when you can QCoreApplication::exec())?
Both functions as well as QThread::exec() are windows into the actual event
dispatcher that runs behind the scenes. All three control the
QAbstractEventDispatcher that either QCoreApplication or QThread creates.

I think these two links answer your question best:

https://code.woboq.org/qt5/qtbase/src/corelib/kernel/
qcoreapplication.cpp.html#_ZN16QCoreApplication4execEv (line 1258)
https://code.woboq.org/qt5/qtbase/src/corelib/thread/
qthread.cpp.html#_ZN7QThread4execEv (line 506)

As you can see, both QCoreApplication::exec or QThread::exec create a
QEventLoop and call exec on it. The same is also true, for that matter, for
QDialog::exec.
Post by Jorge Fierro
- Does QEventLoop::exec() enter *the* main event loop? Or,
- Does QEventLoop::exec() enter a different event loop independent
from QCoreApplication::exec()?
There's only one event dispatcher per thread.
Post by Jorge Fierro
- Can QEventLoop::exec() be called outside QCoreApplication::exec()
(i.e., in a call stack that didn't originate from QCoreApplication::exec())?
Sure. You could even use QEventLoop::exec() in place of
QCoreApplication::exec() in your main function or QThread::exec() in your run
function. The two are very similar, but not identical. QCoreApplication's loop
emits aboutToQuit and its descendant classes react to the last window closing,
while QEventLoop does not.
Post by Jorge Fierro
- If QEventLoop::exec() spins the main loop then any event at all can
be generated. Is this why one must use it very carefully to avoid
reentrancy issues?
Yes. Avoid nesting event loops like the plague.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Konstantin Shegunov
2016-08-03 19:25:32 UTC
Permalink
Post by Thiago Macieira
Post by Jorge Fierro
- If QEventLoop::exec() spins the main loop then any event at all can
be generated. Is this why one must use it very carefully to avoid
reentrancy issues?
Yes. Avoid nesting event loops like the plague.
With, I believe, the important exception being the deferred deletion
events, which are processed only be the last (main) event loop.
Thiago Macieira
2016-08-03 20:41:51 UTC
Permalink
Post by Konstantin Shegunov
Post by Thiago Macieira
Post by Jorge Fierro
- If QEventLoop::exec() spins the main loop then any event at all can
be generated. Is this why one must use it very carefully to avoid
reentrancy issues?
Yes. Avoid nesting event loops like the plague.
With, I believe, the important exception being the deferred deletion
events, which are processed only be the last (main) event loop.
Correct, the deletion events are only processed by an event loop of the same
equally or less nested than when the event was posted. That exists because a
lot of code depends on deleteLater() being much later than what was happening.

It's still a source of errors because the nesting counter can be incremented
in different ways, depending on what triggered the event. So the
recommendation remains: avoid nesting like the plague.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Uwe Rathmann
2016-08-04 04:21:44 UTC
Permalink
Post by Thiago Macieira
It's still a source of errors because the nesting counter can be
incremented in different ways, depending on what triggered the event. So
the recommendation remains: avoid nesting like the plague.
void doIt()
{
...

int option = askForOption();

...

if ( doYouReallyWantTo( option ) )
{
...
}
}

Having to write this code without nested loops ( f.e. QDialog::exec() )
leads to 3 method ...

- before askForOption
- between askForOption and doYouReallyWantTo
- after doYouReallyWantTo

... where you always have the problem to transfer the variables from the
stack ( here option ).

Once your code ends up in sequences of between-GUI-interactions methods
you will have way more problems, than what nested event loops might be.

So yes nested loops might be a problem, but not using them is IMHO the
wrong consequence.

Uwe
Thiago Macieira
2016-08-04 05:31:20 UTC
Permalink
Post by Uwe Rathmann
Having to write this code without nested loops ( f.e. QDialog::exec() )
leads to 3 method ...
- before askForOption
- between askForOption and doYouReallyWantTo
- after doYouReallyWantTo
... where you always have the problem to transfer the variables from the
stack ( here option ).
Once your code ends up in sequences of between-GUI-interactions methods
you will have way more problems, than what nested event loops might be.
So yes nested loops might be a problem, but not using them is IMHO the
wrong consequence.
I disagree. You should try to split. Lambdas might help.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
John Weeks
2016-08-04 16:25:21 UTC
Permalink
At the risk of displaying my ignorance...

Having followed this thread with great interest, I have come to the conclusion that perhaps I'm not really certain what you mean by "nested". Any chance of an example?
Post by Thiago Macieira
I disagree. You should try to split. Lambdas might help.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
_______________________________________________
Interest mailing list
http://lists.qt-project.org/mailman/listinfo/interest
Lambdas have always seemed like a great way to write large blocks of obscure code, but it seems like I'm missing something. Another example?

-John
Thiago Macieira
2016-08-04 17:04:28 UTC
Permalink
Post by John Weeks
At the risk of displaying my ignorance...
Having followed this thread with great interest, I have come to the
conclusion that perhaps I'm not really certain what you mean by "nested".
Any chance of an example?
Starting an event loop from inside another event loop.

exec() → some slot or event handler → exec()
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
John Weeks
2016-08-04 17:32:26 UTC
Permalink
Post by Thiago Macieira
Starting an event loop from inside another event loop.
exec() → some slot or event handler → exec()
OK, so that would cover any use of QEventLoop::exec() in the main thread.

It would also seem to cover any use of QDialog::exec() in the main thread, and QDialog can be used only in the main thread.

-John Weeks
Thiago Macieira
2016-08-04 18:20:58 UTC
Permalink
Post by John Weeks
Post by Thiago Macieira
Starting an event loop from inside another event loop.
exec() → some slot or event handler → exec()
OK, so that would cover any use of QEventLoop::exec() in the main thread.
Correct, unless you did it in the main() function before or instead of
QCoreApplication::main().
Post by John Weeks
It would also seem to cover any use of QDialog::exec() in the main thread,
and QDialog can be used only in the main thread.
Correct. Don't use exec(). Call show() it and return to the existing mainloop.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Frederic Marchal
2016-08-07 07:12:18 UTC
Permalink
Post by Thiago Macieira
Post by John Weeks
On Aug 4, 2016, at 10:04 AM, Thiago Macieira
Starting an event loop from inside another event loop.
exec() → some slot or event handler → exec()
It would also seem to cover any use of QDialog::exec() in the main thread,
and QDialog can be used only in the main thread.
Correct. Don't use exec(). Call show() it and return to the existing mainloop.
That's very restrictive!

It is common (at least to me) to call QFileDialog::getOpenFileName() when
the user clicks on the "Open" menu and then use the returned file name to
carry on the open action. QFileDialog::getOpenFileName() does call
QDialog::exec(). According to you, it must be avoided like the plague
 It
means it renders similar QFileDialog static methods useless.

Yet, I don't see where the problem is. QDialog::exec() displays a modal
dialog. It is not possible to click again on the menu or button that
displayed the dialog in the first place. It effectively breaks recursive
looping, right?

Frederic
André Somers
2016-08-07 08:17:44 UTC
Permalink
Verstuurd vanaf mijn iPhone
Post by Frederic Marchal
Post by Thiago Macieira
Post by John Weeks
Post by Thiago Macieira
Starting an event loop from inside another event loop.
exec() → some slot or event handler → exec()
It would also seem to cover any use of QDialog::exec() in the main thread,
and QDialog can be used only in the main thread.
Correct. Don't use exec(). Call show() it and return to the existing mainloop.
That's very restrictive!
It is common (at least to me) to call QFileDialog::getOpenFileName() when the user clicks on the "Open" menu and then use the returned file name to carry on the open action. QFileDialog::getOpenFileName() does call QDialog::exec(). According to you, it must be avoided like the plague… It means it renders similar QFileDialog static methods useless.
Yet, I don't see where the problem is. QDialog::exec() displays a modal dialog. It is not possible to click again on the menu or button that displayed the dialog in the first place. It effectively breaks recursive looping, right?
Wrong. There are many sources of events, and only the ones related to user input are blocked this way.

André
Post by Frederic Marchal
Frederic
_______________________________________________
Interest mailing list
http://lists.qt-project.org/mailman/listinfo/interest
Thiago Macieira
2016-08-07 17:06:54 UTC
Permalink
Post by André Somers
Post by Frederic Marchal
Yet, I don't see where the problem is. QDialog::exec() displays a modal
dialog. It is not possible to click again on the menu or button that
displayed the dialog in the first place. It effectively breaks recursive
looping, right?
Wrong. There are many sources of events, and only the ones related to user
input are blocked this way.
Indeed. Think of your sockets, that are still receiving data and that is being
processed. Are you sure that your dialog wasn't created as a response to some
data being processed earlier?

Think also of all the timers that will time out.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Konrad Rosenbaum
2016-08-08 06:25:04 UTC
Permalink
Hi,
Post by Thiago Macieira
Post by André Somers
Post by Frederic Marchal
Yet, I don't see where the problem is. QDialog::exec() displays a modal
dialog. It is not possible to click again on the menu or button that
displayed the dialog in the first place. It effectively breaks recursive
looping, right?
Wrong. There are many sources of events, and only the ones related to
user input are blocked this way.
Indeed. Think of your sockets, that are still receiving data and that is
being processed. Are you sure that your dialog wasn't created as a
response to some data being processed earlier?
Think also of all the timers that will time out.
Let me weigh in at this point: yes, it is true - nested event loops make it
very easy to shoot yourself in the foot. Sorry, you are using C++, if you
wanted it safe you would bake bread.

I use them all the time because they make simple algorithms understandable -
event based constructs, even using lambdas, make them horribly hard to
understand. I even wrote a few such methods for my own code.

Here's the choice:

a) avoid exec() at all costs and suffer unreadable code and a lot of the
problems described below simply get hidden in a forest of less readable code

b) follow a few simple rules instead:

- avoid direct delete on QObjects - use deleteLater instead (it is delayed
for the main event loop)

- use high-level methods instead of deletes: e.g. tell a window to close
itself and set it up for automatic cleanup instead of deleting it directly

- try to communicate via signals and set the connections up when you create
the receiver object - Qt will clean the connections up when the receiver is
deleted

- if you absolutely and positively cannot avoid using a pointer - use
QPointer and check it before you call a method

- enable your code to deal with disappearing objects - i.e. let a child
object tell its parent that it is gone and stop any actions that depend on
it when you receive this signal (otherwise you risk endless waiting)

- when you write code that can take a while: think about everything that
could conceivably happen in between and prepare the program for "stuff
happening" - i.e. disappearing objects, more events coming in, things
queuing up, the user losing patience and clicking "Exit", etc.

Qt make it very easy for you to write readable code - I refuse to compromise
this out of paranoia.

Konrad

Jason H
2016-08-03 17:58:45 UTC
Permalink
Sent: Wednesday, August 03, 2016 at 1:22 PM
Subject: [Interest] Relationship between a QEventLoop and QCoreApplication::exec().
Hi. I've been reading through the documentation and mailing list
archives and I haven't found an authoritative and/or conclusive answer to
What exactly is the relationship between an event loop entered by
calling QEventLoop::exec() and *the* main event loop (the one running
when you can QCoreApplication::exec())?
- Does QEventLoop::exec() enter *the* main event loop? Or,
- Does QEventLoop::exec() enter a different event loop independent
from QCoreApplication::exec()?
- Can QEventLoop::exec() be called outside QCoreApplication::exec()
(i.e., in a call stack that didn't originate from
QCoreApplication::exec())?
- If QEventLoop::exec() spins the main loop then any event at all can
be generated. Is this why one must use it very carefully to avoid
reentrancy issues?
As far as I know QCoreApplication::exec() does call QEventLoop::exec().
A thread can have a event loop, and cross-thread signal/slot communication is done via messages in each thread's event loop.
A thread may have more than one event loop. (I think I used this in the past to handle some modal dialog issues. I think while the second event loop is running that the first is unresponsive.)

HTH.
Loading...