From 369ce84301c300fb16983c7681c57696d85d7296 Mon Sep 17 00:00:00 2001 From: Thomas Sondergaard Date: Thu, 1 Aug 2024 12:18:06 +0200 Subject: [PATCH] QTBUG-124496: Work around outdated WM_TIMER events Encode a serial in the timer ID passed to SetTimer(), SetCoalescableTimer() and KillTimer() to work around old WM_TIMER events that may be present in the message queue. Internally Qt will use the regular timer ID, it is only when interfacing with the mentioned WIN32 APIs that the serial is used, and when reacting to WM_TIMER events. This takes advantage of UINT_PTR being 64-bit on 64-bit Windows which allow us to encode a 32-bit serial in the upper 32 bits of the timer ID. --- src/corelib/kernel/qeventdispatcher_win.cpp | 17 ++++++++++------- src/corelib/kernel/qeventdispatcher_win_p.h | 9 ++++++++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index a7663b2481..7298b3a75a 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -191,7 +191,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA if (wp == d->sendPostedEventsTimerId) q->sendPostedEvents(); else - d->sendTimerEvent(wp); + d->sendTimerEvent(WinTimerInfo::timerIdFromWinTimerId(wp), WinTimerInfo::serialFromWinTimerId(wp)); return 0; case WM_QT_SENDPOSTEDEVENTS: Q_ASSERT(d != nullptr); @@ -362,10 +362,10 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) if (!ok) { // user normal timers for (Very)CoarseTimers, or if no more multimedia timers available - ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance); + ok = SetCoalescableTimer(internalHwnd, t->winTimerId(), interval, nullptr, tolerance); } if (!ok) - ok = SetTimer(internalHwnd, t->timerId, interval, nullptr); + ok = SetTimer(internalHwnd, t->winTimerId(), interval, nullptr); if (!ok) qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer"); @@ -379,17 +379,17 @@ void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t) timeKillEvent(t->fastTimerId); QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); } else { - KillTimer(internalHwnd, t->timerId); + KillTimer(internalHwnd, t->winTimerId()); } t->timerId = -1; if (!t->inTimerEvent) delete t; } -void QEventDispatcherWin32Private::sendTimerEvent(int timerId) +void QEventDispatcherWin32Private::sendTimerEvent(int timerId, int serial) { WinTimerInfo *t = timerDict.value(timerId); - if (t && !t->inTimerEvent) { + if (t && !t->inTimerEvent && (serial < 0 || serial == t->winTimerSerial)) { // send event, but don't allow it to recurse t->inTimerEvent = true; @@ -670,6 +670,8 @@ void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier delete sn; } +static std::atomic winTimerSerial = 0; + void QEventDispatcherWin32::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) { #ifndef QT_NO_DEBUG @@ -693,6 +695,7 @@ void QEventDispatcherWin32::registerTimer(int timerId, qint64 interval, Qt::Time WinTimerInfo *t = new WinTimerInfo; t->dispatcher = this; t->timerId = timerId; + t->winTimerSerial = ++winTimerSerial % 0x00ffffff; t->interval = interval; t->timerType = timerType; t->obj = object; @@ -877,7 +880,7 @@ bool QEventDispatcherWin32::event(QEvent *e) return true; } case QEvent::Timer: - d->sendTimerEvent(static_cast(e)->timerId()); + d->sendTimerEvent(static_cast(e)->timerId(), -1); break; default: break; diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index 558490a85e..942ba0961d 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -85,8 +85,15 @@ struct QSockFd { typedef QHash QSFDict; struct WinTimerInfo { // internal timer info + quint64 winTimerId() { return static_cast(timerId) + (static_cast(static_cast(winTimerSerial)) << 32); } + + static int timerIdFromWinTimerId(quint64 winTimerId) { return winTimerId & 0xffffffff; } + static int serialFromWinTimerId(quint64 winTimerId) { return winTimerId >> 32; } + + QObject *dispatcher; int timerId; + int winTimerSerial; qint64 interval; Qt::TimerType timerType; quint64 timeout; // - when to actually fire @@ -126,7 +133,7 @@ public: WinTimerDict timerDict; void registerTimer(WinTimerInfo *t); void unregisterTimer(WinTimerInfo *t); - void sendTimerEvent(int timerId); + void sendTimerEvent(int timerId, int serial); // socket notifiers QSNDict sn_read; -- 2.45.2.windows.1