summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qeventdispatcher_winrt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_winrt.cpp')
-rw-r--r--src/corelib/kernel/qeventdispatcher_winrt.cpp327
1 files changed, 184 insertions, 143 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_winrt.cpp b/src/corelib/kernel/qeventdispatcher_winrt.cpp
index 5e4ef937b9..cc8e961be1 100644
--- a/src/corelib/kernel/qeventdispatcher_winrt.cpp
+++ b/src/corelib/kernel/qeventdispatcher_winrt.cpp
@@ -54,62 +54,20 @@ using namespace ABI::Windows::ApplicationModel::Core;
QT_BEGIN_NAMESPACE
-class QZeroTimerEvent : public QTimerEvent
-{
-public:
- explicit inline QZeroTimerEvent(int timerId)
- : QTimerEvent(timerId)
- { t = QEvent::ZeroTimerEvent; }
-};
-
-struct WinRTTimerInfo // internal timer info
-{
- WinRTTimerInfo(int timerId, int timerInterval, Qt::TimerType timerType, QObject *object, QEventDispatcherWinRT *dispatcher)
- : isFinished(false), id(timerId), interval(timerInterval), timerType(timerType), obj(object), inTimerEvent(false), dispatcher(dispatcher)
- {
- }
-
- void cancel()
+#define INTERRUPT_HANDLE 0
+#define INVALID_TIMER_ID -1
+
+struct WinRTTimerInfo : public QAbstractEventDispatcher::TimerInfo {
+ WinRTTimerInfo(int timerId = INVALID_TIMER_ID, int interval = 0, Qt::TimerType timerType = Qt::CoarseTimer,
+ QObject *obj = 0, quint64 tt = 0) :
+ QAbstractEventDispatcher::TimerInfo(timerId, interval, timerType),
+ inEvent(false), object(obj), targetTime(tt)
{
- if (isFinished) {
- delete this;
- return;
- }
- isFinished = true;
- if (!timer)
- return;
-
- HRESULT hr = timer->Cancel();
- RETURN_VOID_IF_FAILED("Failed to cancel timer");
}
- HRESULT timerExpired(IThreadPoolTimer *)
- {
- if (isFinished)
- return S_OK;
- if (dispatcher)
- QCoreApplication::postEvent(dispatcher, new QTimerEvent(id));
- return S_OK;
- }
-
- HRESULT timerDestroyed(IThreadPoolTimer *)
- {
- if (isFinished)
- delete this;
- else
- isFinished = true;
- return S_OK;
- }
-
- bool isFinished;
- int id;
- int interval;
- Qt::TimerType timerType;
- quint64 timeout; // - when to actually fire
- QObject *obj; // - object to receive events
- bool inTimerEvent;
- ComPtr<IThreadPoolTimer> timer;
- QPointer<QEventDispatcherWinRT> dispatcher;
+ bool inEvent;
+ QObject *object;
+ quint64 targetTime;
};
class QEventDispatcherWinRTPrivate : public QAbstractEventDispatcherPrivate
@@ -121,13 +79,63 @@ public:
~QEventDispatcherWinRTPrivate();
private:
- QHash<int, WinRTTimerInfo*> timerDict;
-
ComPtr<IThreadPoolTimerStatics> timerFactory;
ComPtr<ICoreDispatcher> coreDispatcher;
QPointer<QThread> thread;
- bool interrupt;
+ QHash<int, QObject *> timerIdToObject;
+ QVector<WinRTTimerInfo> timerInfos;
+ QHash<HANDLE, int> timerHandleToId;
+ QHash<int, HANDLE> timerIdToHandle;
+ QHash<int, HANDLE> timerIdToCancelHandle;
+
+ void addTimer(int id, int interval, Qt::TimerType type, QObject *obj,
+ HANDLE handle, HANDLE cancelHandle)
+ {
+ // Zero timer events do not need these handles.
+ if (interval > 0) {
+ timerHandleToId.insert(handle, id);
+ timerIdToHandle.insert(id, handle);
+ timerIdToCancelHandle.insert(id, cancelHandle);
+ }
+ timerIdToObject.insert(id, obj);
+ const quint64 targetTime = qt_msectime() + interval;
+ const WinRTTimerInfo info(id, interval, type, obj, targetTime);
+ if (id >= timerInfos.size())
+ timerInfos.resize(id + 1);
+ timerInfos[id] = info;
+ timerIdToObject.insert(id, obj);
+ }
+
+ bool removeTimer(int id)
+ {
+ if (id >= timerInfos.size())
+ return false;
+
+ WinRTTimerInfo &info = timerInfos[id];
+ if (info.timerId == INVALID_TIMER_ID)
+ return false;
+
+ if (info.interval > 0 && (!timerIdToHandle.contains(id) || !timerIdToCancelHandle.contains(id)))
+ return false;
+
+ info.timerId = INVALID_TIMER_ID;
+
+ // Remove invalid timerinfos from the vector's end, if the timer with the highest id was removed
+ int lastTimer = timerInfos.size() - 1;
+ while (lastTimer >= 0 && timerInfos.at(lastTimer).timerId == INVALID_TIMER_ID)
+ --lastTimer;
+ if (lastTimer >= 0 && lastTimer != timerInfos.size() - 1)
+ timerInfos.resize(lastTimer + 1);
+ timerIdToObject.remove(id);
+ // ... remove handle from all lists
+ if (info.interval > 0) {
+ HANDLE handle = timerIdToHandle.take(id);
+ timerHandleToId.remove(handle);
+ SetEvent(timerIdToCancelHandle.take(id));
+ }
+ return true;
+ }
void fetchCoreDispatcher()
{
@@ -183,8 +191,7 @@ bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags)
if (d->thread && d->thread != QThread::currentThread())
d->fetchCoreDispatcher();
- bool didProcess = false;
- forever {
+ do {
// Process native events
if (d->coreDispatcher) {
boolean hasThreadAccess;
@@ -197,23 +204,32 @@ bool QEventDispatcherWinRT::processEvents(QEventLoop::ProcessEventsFlags flags)
}
// Dispatch accumulated user events
- didProcess = sendPostedEvents(flags);
- if (didProcess)
- break;
+ if (sendPostedEvents(flags))
+ return true;
- if (d->interrupt)
- break;
+ emit aboutToBlock();
+ const QVector<HANDLE> timerHandles = d->timerIdToHandle.values().toVector();
+ DWORD waitResult = WaitForMultipleObjectsEx(timerHandles.count(), timerHandles.constData(), FALSE, 1, TRUE);
+ if (waitResult >= WAIT_OBJECT_0 && waitResult < WAIT_OBJECT_0 + timerHandles.count()) {
+ const HANDLE handle = timerHandles.value(waitResult - WAIT_OBJECT_0);
+ const int timerId = d->timerHandleToId.value(handle);
+ if (timerId == INTERRUPT_HANDLE)
+ break;
- // Short sleep if there is nothing to do
- if (!(flags & QEventLoop::WaitForMoreEvents))
- break;
+ WinRTTimerInfo &info = d->timerInfos[timerId];
+ Q_ASSERT(info.timerId != INVALID_TIMER_ID);
- emit aboutToBlock();
- WaitForSingleObjectEx(GetCurrentThread(), 1, FALSE);
+ QCoreApplication::postEvent(this, new QTimerEvent(timerId));
+
+ // Update timer's targetTime
+ const quint64 targetTime = qt_msectime() + info.interval;
+ info.targetTime = targetTime;
+ emit awake();
+ return true;
+ }
emit awake();
- }
- d->interrupt = false;
- return didProcess;
+ } while (flags & QEventLoop::WaitForMoreEvents);
+ return false;
}
bool QEventDispatcherWinRT::sendPostedEvents(QEventLoop::ProcessEventsFlags flags)
@@ -246,104 +262,121 @@ void QEventDispatcherWinRT::registerTimer(int timerId, int interval, Qt::TimerTy
{
Q_UNUSED(timerType);
-#ifndef QT_NO_DEBUG
if (timerId < 1 || interval < 0 || !object) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::registerTimer: invalid arguments");
+#endif
return;
} else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::registerTimer: timers cannot be started from another thread");
+#endif
return;
}
-#endif
Q_D(QEventDispatcherWinRT);
-
- WinRTTimerInfo *t = new WinRTTimerInfo(timerId, interval, timerType, object, this);
- t->timeout = qt_msectime() + interval;
- d->timerDict.insert(t->id, t);
-
// Don't use timer factory for zero-delay timers
if (interval == 0u) {
- QCoreApplication::postEvent(this, new QZeroTimerEvent(timerId));
+ d->addTimer(timerId, interval, timerType, object, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE);
+ QCoreApplication::postEvent(this, new QTimerEvent(timerId));
return;
}
TimeSpan period;
period.Duration = interval ? (interval * 10000) : 1; // TimeSpan is based on 100-nanosecond units
+ IThreadPoolTimer *timer;
+ const HANDLE handle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE);
+ const HANDLE cancelHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE);
HRESULT hr = d->timerFactory->CreatePeriodicTimerWithCompletion(
- Callback<ITimerElapsedHandler>(t, &WinRTTimerInfo::timerExpired).Get(), period,
- Callback<ITimerDestroyedHandler>(t, &WinRTTimerInfo::timerDestroyed).Get(), &t->timer);
+ Callback<ITimerElapsedHandler>([handle, cancelHandle](IThreadPoolTimer *timer) {
+ DWORD cancelResult = WaitForSingleObjectEx(cancelHandle, 0, TRUE);
+ if (cancelResult == WAIT_OBJECT_0) {
+ timer->Cancel();
+ return S_OK;
+ }
+ if (!SetEvent(handle)) {
+ Q_ASSERT_X(false, "QEventDispatcherWinRT::registerTimer",
+ "SetEvent should never fail here");
+ return S_OK;
+ }
+ return S_OK;
+ }).Get(), period,
+ Callback<ITimerDestroyedHandler>([handle, cancelHandle](IThreadPoolTimer *) {
+ CloseHandle(handle);
+ CloseHandle(cancelHandle);
+ return S_OK;
+ }).Get(), &timer);
if (FAILED(hr)) {
qErrnoWarning(hr, "Failed to create periodic timer");
- delete t;
- d->timerDict.remove(t->id);
+ CloseHandle(handle);
+ CloseHandle(cancelHandle);
return;
}
+ d->addTimer(timerId, interval, timerType, object, handle, cancelHandle);
}
bool QEventDispatcherWinRT::unregisterTimer(int timerId)
{
-#ifndef QT_NO_DEBUG
if (timerId < 1) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::unregisterTimer: invalid argument");
+#endif
return false;
}
if (thread() != QThread::currentThread()) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::unregisterTimer: timers cannot be stopped from another thread");
+#endif
return false;
}
-#endif
+ // As we post all timer events internally, they have to pe removed to prevent stray events
+ QCoreApplicationPrivate::removePostedTimerEvent(this, timerId);
Q_D(QEventDispatcherWinRT);
-
- WinRTTimerInfo *t = d->timerDict.take(timerId);
- if (!t)
- return false;
-
- t->cancel();
- return true;
+ return d->removeTimer(timerId);
}
bool QEventDispatcherWinRT::unregisterTimers(QObject *object)
{
-#ifndef QT_NO_DEBUG
if (!object) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::unregisterTimers: invalid argument");
+#endif
return false;
}
QThread *currentThread = QThread::currentThread();
if (object->thread() != thread() || thread() != currentThread) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::unregisterTimers: timers cannot be stopped from another thread");
+#endif
return false;
}
-#endif
Q_D(QEventDispatcherWinRT);
- for (QHash<int, WinRTTimerInfo *>::iterator it = d->timerDict.begin(); it != d->timerDict.end();) {
- if (it.value()->obj == object) {
- it.value()->cancel();
- it = d->timerDict.erase(it);
- continue;
- }
- ++it;
+ foreach (int id, d->timerIdToObject.keys()) {
+ if (d->timerIdToObject.value(id) == object)
+ unregisterTimer(id);
}
+
return true;
}
QList<QAbstractEventDispatcher::TimerInfo> QEventDispatcherWinRT::registeredTimers(QObject *object) const
{
if (!object) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT:registeredTimers: invalid argument");
+#endif
return QList<TimerInfo>();
}
Q_D(const QEventDispatcherWinRT);
- QList<TimerInfo> list;
- foreach (const WinRTTimerInfo *t, d->timerDict) {
- if (t->obj == object)
- list.append(TimerInfo(t->id, t->interval, t->timerType));
+ QList<TimerInfo> timerInfos;
+ foreach (const WinRTTimerInfo &info, d->timerInfos) {
+ if (info.object == object)
+ timerInfos.append(info);
}
- return list;
+ return timerInfos;
}
bool QEventDispatcherWinRT::registerEventNotifier(QWinEventNotifier *notifier)
@@ -361,27 +394,30 @@ void QEventDispatcherWinRT::unregisterEventNotifier(QWinEventNotifier *notifier)
int QEventDispatcherWinRT::remainingTime(int timerId)
{
-#ifndef QT_NO_DEBUG
if (timerId < 1) {
+#ifndef QT_NO_DEBUG
qWarning("QEventDispatcherWinRT::remainingTime: invalid argument");
+#endif
return -1;
}
-#endif
Q_D(QEventDispatcherWinRT);
- if (WinRTTimerInfo *t = d->timerDict.value(timerId)) {
- const quint64 currentTime = qt_msectime();
- if (currentTime < t->timeout) {
- // time to wait
- return t->timeout - currentTime;
- } else {
- return 0;
- }
- }
-
+ const WinRTTimerInfo timerInfo = d->timerInfos.at(timerId);
+ if (timerInfo.timerId == INVALID_TIMER_ID) {
#ifndef QT_NO_DEBUG
- qWarning("QEventDispatcherWinRT::remainingTime: timer id %d not found", timerId);
+ qWarning("QEventDispatcherWinRT::remainingTime: timer id %d not found", timerId);
#endif
+ return -1;
+ }
+
+ const quint64 currentTime = qt_msectime();
+ if (currentTime < timerInfo.targetTime) {
+ // time to wait
+ return timerInfo.targetTime - currentTime;
+ } else {
+ return 0;
+ }
+
return -1;
}
@@ -392,7 +428,7 @@ void QEventDispatcherWinRT::wakeUp()
void QEventDispatcherWinRT::interrupt()
{
Q_D(QEventDispatcherWinRT);
- d->interrupt = true;
+ SetEvent(d->timerIdToHandle.value(INTERRUPT_HANDLE));
}
void QEventDispatcherWinRT::flush()
@@ -405,55 +441,60 @@ void QEventDispatcherWinRT::startingUp()
void QEventDispatcherWinRT::closingDown()
{
- Q_D(QEventDispatcherWinRT);
- d->timerDict.clear();
}
bool QEventDispatcherWinRT::event(QEvent *e)
{
Q_D(QEventDispatcherWinRT);
- bool ret = false;
switch (e->type()) {
- case QEvent::ZeroTimerEvent:
- ret = true;
- // fall through
case QEvent::Timer: {
QTimerEvent *timerEvent = static_cast<QTimerEvent *>(e);
const int id = timerEvent->timerId();
- if (WinRTTimerInfo *t = d->timerDict.value(id)) {
- if (t->inTimerEvent) // but don't allow event to recurse
- break;
- t->inTimerEvent = true;
+ Q_ASSERT(id < d->timerInfos.size());
+ WinRTTimerInfo &info = d->timerInfos[id];
+ Q_ASSERT(info.timerId != INVALID_TIMER_ID);
- QTimerEvent te(id);
- QCoreApplication::sendEvent(t->obj, &te);
+ if (info.inEvent) // but don't allow event to recurse
+ break;
+ info.inEvent = true;
- if (t = d->timerDict.value(id)) {
- if (t->interval == 0 && t->inTimerEvent) {
- // post the next zero timer event as long as the timer was not restarted
- QCoreApplication::postEvent(this, new QZeroTimerEvent(id));
- }
- t->inTimerEvent = false;
- }
+ QTimerEvent te(id);
+ QCoreApplication::sendEvent(d->timerIdToObject.value(id), &te);
+
+ // The timer might have been removed in the meanwhile
+ if (id >= d->timerInfos.size())
+ break;
+
+ info = d->timerInfos[id];
+ if (info.timerId == INVALID_TIMER_ID)
+ break;
+
+ if (info.interval == 0 && info.inEvent) {
+ // post the next zero timer event as long as the timer was not restarted
+ QCoreApplication::postEvent(this, new QTimerEvent(id));
}
+ info.inEvent = false;
}
default:
break;
}
- return ret ? true : QAbstractEventDispatcher::event(e);
+ return QAbstractEventDispatcher::event(e);
}
QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate()
- : interrupt(false)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
HRESULT hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_System_Threading_ThreadPoolTimer).Get(), &timerFactory);
- if (FAILED(hr))
- qWarning("QEventDispatcherWinRTPrivate::QEventDispatcherWinRTPrivate: Could not obtain timer factory: %lx", hr);
+ Q_ASSERT_SUCCEEDED(hr);
+ HANDLE interruptHandle = CreateEventEx(NULL, NULL, NULL, SYNCHRONIZE|EVENT_MODIFY_STATE);
+ timerIdToHandle.insert(INTERRUPT_HANDLE, interruptHandle);
+ timerHandleToId.insert(interruptHandle, INTERRUPT_HANDLE);
+ timerInfos.reserve(256);
}
QEventDispatcherWinRTPrivate::~QEventDispatcherWinRTPrivate()
{
+ CloseHandle(timerIdToHandle.value(INTERRUPT_HANDLE));
CoUninitialize();
}