Integrating third-party event loop with Qt Event loop
I have an application that is currently built upon the Adaptive Communications Environment (ACE) framework. It uses a Reactor object to supply an event loop. Ultimately I want to port the entire application to Qt and eliminate ACE, so as a first step, I created a QCoreApplication and a QObject-derived application class with the QCoreApplication as its parent. In the main application class, I handle Qt signals, however I also need to service events on the ACE Reactor. I am currently doing this using a single-shot QTimer set to 20ms. When the timeout fires, I call a single-shot version of the ACE event loop and set up a new single-shot timer, and the process repeats. This all works.
The problem I have is the use of the single-shot timer. I don't want this timer firing off when the application is getting ready to shut down, but it is not obvious to me if there is a way to kill this timer at the time I know the app is going away. I could implement a recurring timer with a linked QTimer object, but then I worry about stacked up timeout events if the ACE Reactor call takes too long to execute.
I'm looking for some input on best practice to do something like this.
You could try providing your own implementation of the
Maybe expand a bit on what you mean by "service events on the ACE Reactor" and how ACE works. It might attract better answers than you currently have.
What's wrong with simply stopping a single-shot timer ?
Of course, you have to create your own single-shot timer, don't use the static method QTimer::singleShot().
@kshegunov When the application starts, it makes a call to:
This call has the following implementation:
ACE_Time_Value timeout(0, 10000); // 10ms ACE_Reactor::instance()->handle_events(timeout); QTimer::singleShot(20, this, SLOT(processAceEvents()));
The ACE reactor is essentially an event dispatcher running in a loop. The call:
Is a single shot call to the event dispatcher to service any events generated by the legacy code. By default it is a blocking call, waiting for an event to arrive. I provide a 10ms timeout to allow the call to return, and then set my timer for 20ms to start the process over again.
I will look into the QAbstractEventDispatcher class. Perhaps I can insert this call into the normal event loop and bypass the timer altogether.
Sometime ago I was hacking about the event loop for a (long-term slow-moving hopefully-some-day-to-be-finished project). Anyway, I ended up dropping the idea of interjecting into the event processing in favor of polling. However, in my case I didn't have the ability to set a timeout for the blocking wait-for-event call, so my options were somewhat limited.
Anyway, during my initial attempt I linked against the private API (the usual warning applies) and subclassed the default event dispatcher (I wanted to keep the default processing of timer and socket events). Then I added my own event handling before Qt's in a
processEventsoverride. This worked fine, but it was pointed out that it's not the best way to do things. So I eventually ended up polling the events instead.
It's also noteworthy to mention that Qt wants to "own" the event loop, so even if you do something like the described you may end up smack in the middle of a war who gets events - Qt or ACE
It's not exactly an answer to your question, but I hope it's helpful.
@kshegunov I don't know why I didn't think of this previously, but I came up with a simple solution. In the end, I ended up with a QTimer instance with a recurring timer. However, when a timeout signal is received and the call to:
Is made, I stop the timer. This prevents the stacked timeout issue I was concerned about. I start the timer again, once the call finished:
ace_event_timer->stop(); // prevent any more timer events ACE_Time_Value timeout(0, 10000); // 10ms ACE_Reactor::instance()->handle_events(timeout); ace_event_timer->start(20); //20ms polling
Having the QTime instance, then, allows me to stop the timer upon receipt of the aboutToQuit() signal.
Your insights were good. I learned quite a bit investigating your ideas, before stumbling upon my final solution. Perhaps it will benefit me in the future.
So you ended up polling as well. :)
Btw, I would block the timer signals instead of restarting it (although it should behave pretty much the same way, but it still depends if you're running on one thread or more):
ace_event_timer->blockSignals(true); // prevent any more timer events ACE_Time_Value timeout(0, 10000); // 10ms ACE_Reactor::instance()->handle_events(timeout); ace_event_timer->blockSignals(false); //20ms polling