diff options
author | Bradley T. Hughes <bradley.hughes@nokia.com> | 2012-01-05 14:29:15 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-01-09 09:45:07 +0100 |
commit | d23322bf1cdb28a5ad3163a789bb94287b0dfc9d (patch) | |
tree | 1ba2d20ed532b4f2debc63536d063189a54695b8 /src/plugins | |
parent | c7755d33dbf4b26b208487d4336fca7734bbedcf (diff) |
Change QCocoaEventDispatcher timer handling to use QTimerInfoList
This gives us support for the various Qt::TimerTypes.
We only use one CFRunLoopTimer to drive all of the Qt timers. We update
the time-to-fire for this timer as we add/remove/fire Qt timers. The
documentation for the CFRunLoopTimerSetNextFireDate() function says that
this is a valid use case, and is more performant than constantly adding
and removing CFRunLoopTimers. The documentation recommends using a large
interval for this use case (the docs say "several decades", but we use 1
year).
Change-Id: Ie7fd7a845f4254699a5b6a5720e7626f2c5e787f
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@nokia.com>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.h | 24 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 182 |
2 files changed, 100 insertions, 106 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h index e6e8180085..823a5626fe 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h @@ -92,6 +92,7 @@ #include <QtCore/qstack.h> #include <QtGui/qwindowdefs.h> #include <QtCore/private/qabstracteventdispatcher_p.h> +#include <QtCore/private/qtimerinfo_unix_p.h> #include <CoreFoundation/CoreFoundation.h> @@ -132,21 +133,6 @@ public: void flush(); }; -struct MacTimerInfo { - QCocoaEventDispatcherPrivate *d_ptr; - int id; - int interval; - Qt::TimerType timerType; - QObject *obj; - bool pending; - CFRunLoopTimerRef runLoopTimer; - bool operator==(const MacTimerInfo &other) - { - return (id == other.id); - } -}; -typedef QHash<int, MacTimerInfo *> MacTimerHash; - struct MacSocketInfo { MacSocketInfo() : socket(0), runloop(0), readNotifier(0), writeNotifier(0) {} CFSocketRef socket; @@ -163,7 +149,12 @@ class QCocoaEventDispatcherPrivate : public QAbstractEventDispatcherPrivate public: QCocoaEventDispatcherPrivate(); - MacTimerHash macTimerHash; + // timer handling + QTimerInfoList timerInfoList; + CFRunLoopTimerRef runLoopTimerRef; + void maybeStartCFRunLoopTimer(); + void maybeStopCFRunLoopTimer(); + static void activateTimer(CFRunLoopTimerRef, void *info); // Set 'blockSendPostedEvents' to true if you _really_ need // to make sure that qt events are not posted while calling @@ -196,7 +187,6 @@ public: static Boolean postedEventSourceEqualCallback(const void *info1, const void *info2); static void postedEventsSourcePerformCallback(void *info); - static void activateTimer(CFRunLoopTimerRef, void *info); static void waitingObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info); static void firstLoopEntry(CFRunLoopObserverRef ref, CFRunLoopActivity activity, void *info); diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index d46e25499c..5c22050711 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -111,32 +111,79 @@ static inline CFRunLoopRef mainRunLoop() /* timer call back */ void QCocoaEventDispatcherPrivate::activateTimer(CFRunLoopTimerRef, void *info) { - MacTimerInfo *tmr = static_cast<MacTimerInfo *>(info); - QCocoaEventDispatcherPrivate *d = tmr->d_ptr; - int timerID = tmr->id; - if (tmr == 0 || tmr->pending == true) - return; // Can't send another timer event if it's pending. - - if (d->blockSendPostedEvents) { - QCoreApplication::postEvent(tmr->obj, new QTimerEvent(tmr->id)); + QCocoaEventDispatcherPrivate *d = static_cast<QCocoaEventDispatcherPrivate *>(info); + (void) d->timerInfoList.activateTimers(); + d->maybeStartCFRunLoopTimer(); +} + +void QCocoaEventDispatcherPrivate::maybeStartCFRunLoopTimer() +{ + if (timerInfoList.isEmpty()) { + // no active timers, so the CFRunLoopTimerRef should not be active either + Q_ASSERT(runLoopTimerRef == 0); + return; + } + + if (runLoopTimerRef == 0) { + // start the CFRunLoopTimer + CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); + CFTimeInterval interval; + CFTimeInterval oneyear = CFTimeInterval(3600. * 24. * 365.); + + // Q: when should the CFRunLoopTimer fire for the first time? + struct timeval tv; + if (timerInfoList.timerWait(tv)) { + // A: when we have timers to fire, of course + interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + } else { + // this shouldn't really happen, but in case it does, set the timer to fire a some point in the distant future + interval = oneyear; + } + + ttf += interval; + CFRunLoopTimerContext info = { 0, this, 0, 0, 0 }; + // create the timer with a large interval, as recommended by the CFRunLoopTimerSetNextFireDate() + // documentation, since we will adjust the timer's time-to-fire as needed to keep Qt timers working + runLoopTimerRef = CFRunLoopTimerCreate(0, ttf, oneyear, 0, 0, QCocoaEventDispatcherPrivate::activateTimer, &info); + Q_ASSERT(runLoopTimerRef != 0); + + CFRunLoopAddTimer(mainRunLoop(), runLoopTimerRef, kCFRunLoopCommonModes); } else { - tmr->pending = true; - QTimerEvent e(tmr->id); - - QCoreApplication::sendSpontaneousEvent(tmr->obj, &e); - // Get the value again in case the timer gets unregistered during the sendEvent. - tmr = d->macTimerHash.value(timerID); - if (tmr != 0) - tmr->pending = false; + // calculate when we need to wake up to process timers again + CFAbsoluteTime ttf = CFAbsoluteTimeGetCurrent(); + CFTimeInterval interval; + + // Q: when should the timer first next? + struct timeval tv; + if (timerInfoList.timerWait(tv)) { + // A: when we have timers to fire, of course + interval = qMax(tv.tv_sec + tv.tv_usec / 1000000., 0.0000001); + } else { + // no timers can fire, but we cannot stop the CFRunLoopTimer, set the timer to fire at some + // point in the distant future (the timer interval is one year) + interval = CFRunLoopTimerGetInterval(runLoopTimerRef); + } + + ttf += interval; + CFRunLoopTimerSetNextFireDate(runLoopTimerRef, ttf); } +} +void QCocoaEventDispatcherPrivate::maybeStopCFRunLoopTimer() +{ + if (runLoopTimerRef == 0) + return; + + CFRunLoopTimerInvalidate(runLoopTimerRef); + CFRelease(runLoopTimerRef); + runLoopTimerRef = 0; } void QCocoaEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *obj) { #ifndef QT_NO_DEBUG if (timerId < 1 || interval < 0 || !obj) { - qWarning("QEventDispatcherMac::registerTimer: invalid arguments"); + qWarning("QCocoaEventDispatcher::registerTimer: invalid arguments"); return; } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QObject::startTimer: timers cannot be started from another thread"); @@ -144,58 +191,37 @@ void QCocoaEventDispatcher::registerTimer(int timerId, int interval, Qt::TimerTy } #endif - MacTimerInfo *t = new MacTimerInfo(); - t->d_ptr = d_func(); - t->id = timerId; - t->interval = interval; - t->timerType = timerType; - t->obj = obj; - t->runLoopTimer = 0; - t->pending = false; - - CFAbsoluteTime fireDate = CFAbsoluteTimeGetCurrent(); - CFTimeInterval cfinterval = qMax(CFTimeInterval(interval) / 1000, 0.0000001); - fireDate += cfinterval; - t->d_ptr->macTimerHash.insert(timerId, t); - CFRunLoopTimerContext info = { 0, (void *)t, 0, 0, 0 }; - t->runLoopTimer = CFRunLoopTimerCreate(0, fireDate, cfinterval, 0, 0, - QCocoaEventDispatcherPrivate::activateTimer, &info); - if (t->runLoopTimer == 0) { - qFatal("QEventDispatcherMac::registerTimer: Cannot create timer"); - } - CFRunLoopAddTimer(mainRunLoop(), t->runLoopTimer, kCFRunLoopCommonModes); + Q_D(QCocoaEventDispatcher); + d->timerInfoList.registerTimer(timerId, interval, timerType, obj); + d->maybeStartCFRunLoopTimer(); } -bool QCocoaEventDispatcher::unregisterTimer(int identifier) +bool QCocoaEventDispatcher::unregisterTimer(int timerId) { #ifndef QT_NO_DEBUG - if (identifier < 1) { - qWarning("QEventDispatcherMac::unregisterTimer: invalid argument"); + if (timerId < 1) { + qWarning("QCocoaEventDispatcher::unregisterTimer: invalid argument"); return false; } else if (thread() != QThread::currentThread()) { qWarning("QObject::killTimer: timers cannot be stopped from another thread"); return false; } #endif - if (identifier <= 0) - return false; // not init'd or invalid timer - - MacTimerInfo *timerInfo = d_func()->macTimerHash.take(identifier); - if (timerInfo == 0) - return false; - CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); - CFRelease(timerInfo->runLoopTimer); - delete timerInfo; - - return true; + Q_D(QCocoaEventDispatcher); + bool returnValue = d->timerInfoList.unregisterTimer(timerId); + if (!d->timerInfoList.isEmpty()) + d->maybeStartCFRunLoopTimer(); + else + d->maybeStopCFRunLoopTimer(); + return returnValue; } bool QCocoaEventDispatcher::unregisterTimers(QObject *obj) { #ifndef QT_NO_DEBUG if (!obj) { - qWarning("QEventDispatcherMac::unregisterTimers: invalid argument"); + qWarning("QCocoaEventDispatcher::unregisterTimers: invalid argument"); return false; } else if (obj->thread() != thread() || thread() != QThread::currentThread()) { qWarning("QObject::killTimers: timers cannot be stopped from another thread"); @@ -204,40 +230,26 @@ bool QCocoaEventDispatcher::unregisterTimers(QObject *obj) #endif Q_D(QCocoaEventDispatcher); - MacTimerHash::iterator it = d->macTimerHash.begin(); - while (it != d->macTimerHash.end()) { - MacTimerInfo *timerInfo = it.value(); - if (timerInfo->obj != obj) { - ++it; - } else { - CFRunLoopTimerInvalidate(timerInfo->runLoopTimer); - CFRelease(timerInfo->runLoopTimer); - delete timerInfo; - it = d->macTimerHash.erase(it); - } - } - return true; + bool returnValue = d->timerInfoList.unregisterTimers(obj); + if (!d->timerInfoList.isEmpty()) + d->maybeStartCFRunLoopTimer(); + else + d->maybeStopCFRunLoopTimer(); + return returnValue; } QList<QCocoaEventDispatcher::TimerInfo> QCocoaEventDispatcher::registeredTimers(QObject *object) const { +#ifndef QT_NO_DEBUG if (!object) { - qWarning("QEventDispatcherMac:registeredTimers: invalid argument"); + qWarning("QCocoaEventDispatcher:registeredTimers: invalid argument"); return QList<TimerInfo>(); } - - QList<TimerInfo> list; +#endif Q_D(const QCocoaEventDispatcher); - MacTimerHash::const_iterator it = d->macTimerHash.constBegin(); - while (it != d->macTimerHash.constEnd()) { - MacTimerInfo *t = it.value(); - if (t->obj == object) - list << TimerInfo(t->id, t->interval, t->timerType); - ++it; - } - return list; + return d->timerInfoList.registeredTimers(object); } /************************************************************************** @@ -882,7 +894,8 @@ void QCocoaEventDispatcherPrivate::endModalSession(QWindow *window) } QCocoaEventDispatcherPrivate::QCocoaEventDispatcherPrivate() - : blockSendPostedEvents(false), + : runLoopTimerRef(0), + blockSendPostedEvents(false), currentExecIsNSAppRun(false), nsAppRunCalledByQt(false), cleanupModalSessionsNeeded(false), @@ -1045,18 +1058,9 @@ void QCocoaEventDispatcher::flush() QCocoaEventDispatcher::~QCocoaEventDispatcher() { Q_D(QCocoaEventDispatcher); - //timer cleanup - MacTimerHash::iterator it = d->macTimerHash.begin(); - while (it != d->macTimerHash.end()) { - MacTimerInfo *t = it.value(); - if (t->runLoopTimer) { - CFRunLoopTimerInvalidate(t->runLoopTimer); - CFRelease(t->runLoopTimer); - } - delete t; - ++it; - } - d->macTimerHash.clear(); + + qDeleteAll(d->timerInfoList); + d->maybeStopCFRunLoopTimer(); // Remove CFSockets from the runloop. for (MacSocketHash::ConstIterator it = d->macSockets.constBegin(); it != d->macSockets.constEnd(); ++it) { |