PDA

View Full Version : Problems with QApplication::processEvents()



Winni
2nd January 2008, 20:53
Hi,

I am currently working on a project for my diploma.
My job is it to extend a qt standalone application to something vnc-like client/server application for remote support.
What I have done so far:
I have installed an event filter on the client side that catches mouse/user events.
After serialization the captured event is send with some qwidget information (name, size etc.) over a qtcpsocket to the remote peer.
There the event is deserialized and the qwidget is identified by the details (doing some search in QApplication::allWidgets). The event is finally posted via QCoreApplication:: postEvent method.
Everything works as expected so far.

But now I found that there are two cases in which the above described approach does not seem to work and I cannot figure out what happens:

When the application is running into a while loop and QApplication:: processEvents is called within this loop or a QDialog is executed (not shown) elsewhere instead, cpu usage becomes 100% after a while and finally the peer application crashes. What seems very strange to me is that the cpu usage increases and the application crashes even if no mouse events are send to the peer (no user / socket interaction in that case, just an open (modal) QDialog...)

The problem seems to be the call to processEvents as this is used in the exec() method of QDialog, too.

Can someone give me some hints what might be the problem in this case?

Thank you in advance.

Winni

high_flyer
2nd January 2008, 22:12
When the application is running into a while loop and QApplication:: processEvents is called within this loop or a QDialog is executed (not shown) instead, cpu usage becomes 100% after a while
This has nothing to do with QApplication::processEvents() but with the fact you are in a busy while loop.
To test this, just comment out QApplication::processEvents() in the while loop and see if the CPU usage go up still.

I couldn't quite follow if the while loop is on the 'remote peer' or on the local 'client'.

It would be great if you could post some relevant code.

Am I to understand that no threads are involved?
Does it mean, that only one client/server connection can exist at any time?

In general, such cases are dealt with threads, where each thread deals with one client on the server side, removing the need for hungry while loops.

Winni
2nd January 2008, 23:03
The while loop is not the problem, as the cpu usage keeps normal when there is no remote connection.

Both applications are just the same. Both have got the same while loop in this case.
As metioned the connection should be used for remote support.
One cannot distinguish between client and server after connection has been established. If a user interacts with his local application, the interaction is reproduced on the remote peer's side and vice versa.
Works great so far :D

As the main application code was not written by me I am not allowed to post original code here, sadly.

The while loop looks a bit strange... well, not my code ;)



someClass::buttonXYZClicked()
{
bool isOK=true;
...
this->show();
this->setActiveWindow();
while (isOK)
{
QCoreApplication::processEvents();
....
if(buttonCancel->isDown())
{
isOK=false;
}
...
Sleep(1);
...
}//END while
}//END someClass::buttonXYZClicked()



If I delete the processEvent call I cannot interact with the widget any longer.
As it is not my job to debug the main application I just do not want to hang around with the main code to much.
When I increase the value for sleep to a more realistic value, the problem is not that bad, but still there.

As written before, the problem is the same if both peers show the "same" modal QDialog with no user/socket activity in the meanwhile. The cpu usage increases and the remote peer crashes after a while anyway.

jacek
2nd January 2008, 23:38
When someClass::buttonXYZClicked() is invoked? Is it possible that the program flow renters this method before processEvents() returns? Do you have more such loops?

Winni
3rd January 2008, 00:20
No, the method is just called once (when the button is pressed down).

When I first start with my code I tried to identify qwidgets by their x/y-position via QApplication::widgetAt(QPoint). If I remember rightly I have not had this problem but others (same screen resolution was needed etc.).
I suppose that there might be a problem when interating over QApplication::allWidgets() in combination with the processEvent calls elsewhere in the main application.

I have no other loops found that way but some QDialogs are executed, which do not work either.

mchara
3rd January 2008, 07:13
Hi, first QDialogs::exec()


Enters the main event loop and waits until exit() is called or the main widget is destroyed, and returns the value that was set to exit() (which is 0 if exit() is called via quit()).
It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets.
Generally speaking, no user interaction can take place before calling exec(). As a special case, modal widgets like QMessageBox can be used before calling exec(), because modal widgets call exec() to start a local event loop.

note "modal widgets call exec() to start a local event loop."
so this explains behavior QDialog::exec() behavior in your case.
But i have no idea how to catch events from Qdialogs local loops so far.

as for processEvents in local loop i found only this:


In event you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.

I'm not sure this will be helpful but it's worth a try.

If sendPostedEvents() won't help, you may try serialize system specific messages instead of qt events (i.e. virtual bool QCoreApplication::winEventFilter ( MSG * msg, long * result ) ) they should be independent of local event loops in application.


One more thing you can check is type of connection between peers, if it's blocking you may cause dead lock in local loops. On the other hand app can be unstable if some events in queue are dependent and one of them will be lost in transmission. Just better to be sure...

high_flyer
3rd January 2008, 09:16
The while loop is not the problem, as the cpu usage keeps normal when there is no remote connection.
Ok with out splitting hairs here, then would it be correct to say that the problem is IN the while loop when the connection is alive?
I ask, since then it might point (probably) to the way you are doing the peer to peer communication in the loop, which effects the flow and speed of the loop.
Can you show relevant code that is doing the communication in the loop?
Do you have blocking calls in it when the connection is alive?

Winni
3rd January 2008, 13:58
Posting the code is a bit difficult as there are several places where client server communication is done.
I will try to explain how it works:
If there is a mouse event captured in the event filter of peer 1 a signal writeSocket(object,event) is emitted.
At the end of this writeSocket method waitForReadyRead() is called which blocks peer 1 until it receives some bytes from peer 2. When peer 2 receives the bytes from peer 1 the signal receivedMessage(objectDetails,event) is emitted (no waitFor… here). This signal is connected to an eventDispatcher method, which finally posts the event for a specific qwidget. If the posted event passes the QApplication::notify(receiver,event) method a message ("OK") is send to peer 1 which unblocks it. Of course there is some work done to avoid local user events at peer 2 in the meanwhile (no deadlocks).

I have played around and found that the peer 2 application does not really crash when executing a dialog during remote connection. What happens is that the application seems to become very busy after a while (without any interaction) which slows down event processing. So the "ok" message from peer 2 is send extremely delayed but everything still works in principal. As soon as the dialog is closed everythings works fine.

What I have not mentioned so far is that one can interact with the dialog even if cpu usage increases. If a critical level is reached above mentioned behavior occurs.

In fact, I cannot figure out what slows down the event processing that much.

high_flyer
3rd January 2008, 14:02
what I meant was the code in the while loop.
Do you have blocking calls in it?
Some more code, specially code that has to do with peer communication IN the while loop, would be interesting to see.

Winni
3rd January 2008, 14:09
There is no communication code in the while loop.
The while loop is taken from the original standalone application.
It shows a window for some user inputs.

The whole communication is done in the above described way.

mchara
3rd January 2008, 14:20
Did you tried with non blocking communication? I think slowdowns can be caused by synchronization of both peers.

You may try to post events from first peer to 2 thread with some events buffer, that would deal with communication with 2 thread of 2 peer(also with buffer) so overloaded connection would slowdown only additional threads (while there would still be a number of events in buffers). In such approach only additional threads would be blocking and peers would post/receive events to/from buffers in non blocking way...

p.s.
dialogs uses local loops and main event loop is theoretically suspended(maybe it's processed rarely from dialog's local loop)... are you sure that second peer receives also modal dialog's events? Because if it is, there are some differences between qt manual and real implementation.

Winni
3rd January 2008, 15:00
Non blocking sockets do not work as peer 1 needs to wait until peer 2 has posted the event.
Otherwise both applications run out of sync. With this approach no buffering is needed.

The exec method of QDialog is a while loop, too. Within this loop there is also a call to processEvents. This call awakes the main event loop, I think.
So, the posted while loop above and the while loop in the exec method are the same in principal, which explains the same behavior. The only difference is that there is no sleep call in the exec-while loop. Because of that cpu usage reaches 100% quite fast when a QDialog is executed.

The processEvent call in the exec method is the reason why a remote interaction with the dialog during a remote connection is possible, I think.

Winni
3rd January 2008, 18:21
Maybe I have found the reason but no solution yet



processEvents():
Processes all pending events for the calling thread according to the specified flags until there are no more events to process.You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file). Calling this function processes events only for the calling thread.


As the qTcpSocket seems to live in its own thread, its events are not processed when processEvents is called because the dialogs are in a different thread.
Maybe everything just works in this case because the readyRead() signal "awakes" the socket thread by time. The remote events that arrive at the peers socket are then posted to the main event loop via postEvent. So they are processed when processEvents is called.

jacek
3rd January 2008, 21:11
Judging from the description of application's behaviour I would say that your application falls into some kind of recursion and events are contantly added to the event queue until the program crashes. Have you seen what's happening with the memory usage? Does it grow? Did you try to use a debugger to see where it exactly crashes and to examine the backtrace?

mchara
4th January 2008, 06:41
Maybe I have found the reason but no solution yet



As the qTcpSocket seems to live in its own thread, its events are not processed when processEvents is called because the dialogs are in a different thread.
Maybe everything just works in this case because the readyRead() signal "awakes" the socket thread by time. The remote events that arrive at the peers socket are then posted to the main event loop via postEvent. So they are processed when processEvents is called.

But if QTcpSocket have own thread, it's event loop works even if main threads events are not processed i.e. because of executed dialog or while loop.

Winni
5th January 2008, 19:31
I have found the reason for my "high cpu usage problem" :D
It is no Qt matter, in fact.
There is some kind of "task manager" in the main application.
Programmers can add own tasks which are going to be called regularly.
The task managers method is startet every 5 ms per QTimerEvent.

Increasing the timeout for this "manager" did the job :crying:

Thanks to all for your help.

Winni