PDA

View Full Version : QCoreApplication + event looping



ct
28th May 2011, 00:17
I'm a bit confused about the way we should be using QCoreApplication::exec() for event looping. Here is how I understand the concept in terms of QApplication


int main(int argc,char** argv)
{
QApplication app(argc, argv);
QWidget wgt;
wgt.show();
return app.exec();
}

Here, wgt.show() is a non-blocking function and app.exec() is executed right after wgt.show() executes. Which means that everything works fine and we can use click button or other events that are emitted by the widget because app.exec() has already been executed and event looping can be processed.

How does this work in case of QCoreApplication ?
For example


int main(int argc,char** argv)
{
QCoreApplication app(argc, argv);
MyClass obj;
QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()),Qt::QueuedConnection);
obj.ProcessSomething();
return app.exec();
}


Here, ProcessSomething() does a lot of things including emitting signals and assigning them to slots. However, those signals are emitted from QProcess and other Qt Objects won't work since app.exec() has not been executed. My question is how to create a non-blocking function in case of console application where we are not waiting for user input. If we move app.exec() above processing, then the application will halt.
The problem here is that there is no GUI that will wait for user action on objects, I've to somehow initiate the event looping before doing the processing and I don't know how ?

DanH
28th May 2011, 00:50
If the system doesn't prevent it (because you lock out the UI for too long), you can do as much as you want in sequential logic before you get to the exec(). However, not until you get to exec() (or to a processEvents() call) are many of the events and signals handled, so any of your logic that depends on them will be broken.

You can insert your own processEvents() calls at appropriate points in your code, to let the event/signal handling occur, and if done judiciously this will permit sequential logic to operate while using events/signals, but it's meaningless to say you could "initiate event looping", since that's not an asyncronous activity but rather a literal loop inside exec().

Another choice is to construct your sequential logic using a state machine, so that each "atomic" operation triggers the next through event or signal. Sometimes this is actually an effective approach, allowing pseudo-parallel processing techniques, etc, and sometimes it's at least an interesting intellectual exercise. But mostly it's a drag.

You can of course stick sequential logic in a separate thread, to allow it to execute in parallel with the UI, but then it will not be able to interact with the UI or use events/signals.

ct
28th May 2011, 01:49
Well it works now. I was just emitting the done() SIGNAL before the QProcess finished its command. But now the question is how are the signal/slots of QProcess working here as app.exec() has not been executed yet and we are still inside ProcessSomething(). Is it due to the fact that until the QProcess executes its command app.exec() has been executed ? Or am I totally missing something here ? Also is there a nice way to do this ?

DanH
28th May 2011, 03:37
Most (but not all) signals will work, and a few (but not most) events will work without an event loop. But for general purposes you need one. (Though note that some system calls you make may contain calls to processEvents() within them.)

QProcess isn't doing anything -- it's just the container for your executing code, and it's "command" is your program. You execute QCoreApplication.exec() when you see fit, to enable UI interaction. For a program with no UI interaction (and no other signal/event processing) there's no need for the exec() call at all.

Understand that exec() basically is just:

while (!quit) {
processEvents();
}
with logic to wait (vs burn CPU cycles) if no events are posted.

ct
28th May 2011, 05:54
Is there a way to find out what kind of signals/event work and which don't work ? Here I'm talking about the finished signal sent by QProcess which is working.

wysota
28th May 2011, 08:44
The general rule is that signals that are emitted as a result of some lengthy process will not work and signals that are emitted as a direct result of a computation that takes place immediately will work.

And for completeness, you can do this:

class MyClass : public QObject {
Q_OBJECT
public:
// ...
public slots:
void ProcessSomething(){...}
};

int main(int argc,char** argv)
{
QCoreApplication app(argc, argv);
MyClass obj;
QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()),Qt::QueuedConnection);
QTimer::singleShot(0, &obj, SLOT(ProcessSomething()));
return app.exec();
}
This will make your "ProcessSomething" method execute after app.exec() is called, when the event loop is already running.

ct
28th May 2011, 09:32
Thanks, so by using
QTimer::singleShot(0, &obj, SLOT(ProcessSomething())); we can be sure that the events are handled properly. I did use this method earlier but was not sure whether it was any different form just calling ProcessSomething() as both was working fine. But there could be cases where only QTimer::singleShot would work. One question though, isn't the 0 suppose to mean that the slot is to be executed immediately, or does it have a special meaning ? I didn't find anything on this in the manual.

Santosh Reddy
28th May 2011, 10:18
One question though, isn't the 0 suppose to mean that the slot is to be executed immediately, or does it have a special meaning ? I didn't find anything on this in the manual.

"0" does mean to execute immediately, but Qt executes it as soon as possible, that is it has to enter the event loop, only then Qt can respect (execute) the QTimer events, as you might have alrady figured out the event loop only starts inside exec()

Just as a reference QTimer documentation says

As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed.

DanH
28th May 2011, 13:25
The writeup on signals and slots (http://doc.qt.nokia.com/4.7/signalsandslots.html)in the Qt documentation will give some of the details about when signals are processed, but in general you can't count on them being immediate calls and you can't count on them not being immediate calls.

Normally when you do a connect() with no connection type parm, signals within your code will be immediate if within a single thread. But many signals from the UI are effectively from a different thread and will be queued.


The general rule is that signals that are emitted as a result of some lengthy process will not work and signals that are emitted as a direct result of a computation that takes place immediately will work.

And for completeness, you can do this:

class MyClass : public QObject {
Q_OBJECT
public:
// ...
public slots:
void ProcessSomething(){...}
};

int main(int argc,char** argv)
{
QCoreApplication app(argc, argv);
MyClass obj;
QObject::connect(&obj, SIGNAL(done()), &app, SLOT(quit()),Qt::QueuedConnection);
QTimer::singleShot(0, &obj, SLOT(ProcessSomething()));
return app.exec();
}
This will make your "ProcessSomething" method execute after app.exec() is called, when the event loop is already running.

But then "ProcessSomething" will lock out event processing while it runs, and it's still apt to encounter problems if its own logic depends on the event loop running.

wysota
28th May 2011, 17:16
Normally when you do a connect() with no connection type parm, signals within your code will be immediate if within a single thread. But many signals from the UI are effectively from a different thread and will be queued.
This is not accurate. Signals are never queued and are always emitted immediately when a particular "emit x()" line is executed. Only slots can be queued.

DanH
29th May 2011, 12:59
Whether a signal is queued or not is a function of the type of connect() used and whether the sender and receiver are in the same thread or different threads. No attribute of slot controls this.

wysota
29th May 2011, 18:07
Whether a signal is queued or not is a function of the type of connect() used and whether the sender and receiver are in the same thread or different threads.

Ok, so I have one signal connected to two slots, one slot is in the same thread as the object emitting the signal and the other slot is in a different thread. I now call "emit x()". When will the signal be emitted?


No attribute of slot controls this.
No attribute of signal controls this. It is an attribute of the connection which is responsible for triggering the slot. If you answer my question above, you'll understand it is the slot that is executed later, not the signal.

DanH
30th May 2011, 04:46
Ok, so I have one signal connected to two slots, one slot is in the same thread as the object emitting the signal and the other slot is in a different thread. I now call "emit x()". When will the signal be emitted?

Likely the signial in the same thread will be executed immediately, while the one in the different thread will be queued. But it could be that they both end up being queued.


No attribute of signal controls this. It is an attribute of the connection which is responsible for triggering the slot. If you answer my question above, you'll understand it is the slot that is executed later, not the signal.
There is a terminology problem here. There are four entities to consider: The emit, the slot, the connection, and the "signal", which is essentially a message. It is the delivery of this message to a slot which constitutes "executing" the signal. If something is queued, it is the signal.

Qt::AutoConnection 0 (default) If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection. Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection. The type of connection is determined when the signal is emitted.

wysota
30th May 2011, 07:03
Likely the signial in the same thread will be executed immediately, while the one in the different thread will be queued. But it could be that they both end up being queued.
Don't guess. If you don't know, rely on what someone else is saying or check in the source code. The signal (the method declared in the "signals" section) is executed only once. There can be zero or more slots connected to the signal and each of them will be executed when the right time comes. What you say wouldn't make sense if you had a signal connected to two slots living in different threads. How would the signal be emitted then? Would it be stalled until both threads were in their event loops at the same time? What if one of the threads wasn't running a loop at all?


There is a terminology problem here.
It is not, if you write what you have written above. And if a question is asked about which signals are deferred and which are emitted immediately. The question was about delays related to timers and networking operations (and other asynchronous operations) and not about slot execution and regardless of what lives in which thread. Networking signals (apart those signalling errors) will always be deferred despite the fact the internal connections are made within the same thread. The same goes with QtConcurrent signals, for instance.


There are four entities to consider: The emit, the slot, the connection, and the "signal", which is essentially a message. It is the delivery of this message to a slot which constitutes "executing" the signal.
No. You can invoke the slot without even having the signal by calling QMetaObject::invokeMethod() and it accepts an argument telling how the slot should be invoked (as a direct or queued connection albeit there is no connection there at all) and processing is exactly the same as with a signal, you also end up with the same QMetaObject::metacall() call.


If something is queued, it is the signal.
You are guessing again. And missing. The signal is emitted and when the slot is in another thread, it is transformed into an event and put in the event loop of the destination thread. When the thread processes its events, the data will be marshalled based on the event and the slot will be called. Look at the source code generated by the moc for a signal, it looks essentially like this:

// SIGNAL 0
void CMSMessageListener::messageReceived(CMSMessage _t1)
{
void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

There is no check for threads here, the signal is activated immediately. Then activate() loops through the list of connections and either queues the metacall event or executes the slot immediately.

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv){
// ...
do {
QObjectPrivate::Connection *c = connectionLists->at(signal_index).first;
// ...
do {
// ...
QObject * const receiver = c->receiver;
// ...
if ((c->connectionType == Qt::AutoConnection
&& (currentThreadData != sender->d_func()->threadData
|| receiver->d_func()->threadData != sender->d_func()->threadData))
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
continue;
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
blocking_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
continue;
}
const int method = c->method;
// ...
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
} while (...);
// ...
} while (...);


If something is queued, it is the signal.
To be precise it is a QMetaCallEvent that is queued. And considering the fact that if you have two connections to the same ("alien") thread you'll be getting two such events then I'd lean towards saying that it is the slot invokation that is being queued (especially that the event carries the slot id).