diff options
Diffstat (limited to 'src/corelib/kernel/qeventdispatcher_win.cpp')
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_win.cpp | 526 |
1 files changed, 173 insertions, 353 deletions
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 43872fd027..a7663b2481 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -1,63 +1,22 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qeventdispatcher_win_p.h" #include "qcoreapplication.h" #include <private/qsystemlibrary_p.h> #include "qoperatingsystemversion.h" -#include "qpair.h" #include "qset.h" #include "qsocketnotifier.h" #include "qvarlengtharray.h" -#include "qwineventnotifier.h" #include "qelapsedtimer.h" #include "qcoreapplication_p.h" #include <private/qthread_p.h> -#include <private/qwineventnotifier_p.h> QT_BEGIN_NAMESPACE -extern uint qGlobalPostedEventsCount(); - #ifndef TIME_KILL_SYNCHRONOUS # define TIME_KILL_SYNCHRONOUS 0x0100 #endif @@ -96,27 +55,26 @@ class QEventDispatcherWin32Private; LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); +static quint64 qt_msectime() +{ + using namespace std::chrono; + auto t = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()); + return t.count(); +} + QEventDispatcherWin32Private::QEventDispatcherWin32Private() - : threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0), - getMessageHook(0), sendPostedEventsTimerId(0), wakeUps(0), - activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL) + : interrupt(false), internalHwnd(0), + sendPostedEventsTimerId(0), wakeUps(0), + activateNotifiersPosted(false) { } QEventDispatcherWin32Private::~QEventDispatcherWin32Private() { - if (winEventNotifierActivatedEvent) - CloseHandle(winEventNotifierActivatedEvent); if (internalHwnd) DestroyWindow(internalHwnd); } -void QEventDispatcherWin32Private::activateEventNotifier(QWinEventNotifier * wen) -{ - QEvent event(QEvent::WinEventAct); - QCoreApplication::sendEvent(wen, &event); -} - // This function is called by a workerthread void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, DWORD_PTR user, DWORD_PTR /*reserved*/, DWORD_PTR /*reserved*/) { @@ -127,18 +85,6 @@ void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/, QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId)); } -static inline UINT inputQueueMask() -{ - UINT result = QS_ALLEVENTS; - // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of - // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8. -#if WINVER > 0x0601 - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) - result &= ~(QS_TOUCH | QS_POINTER); -#endif // WINVER > 0x0601 - return result; -} - LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_NCCREATE) @@ -150,11 +96,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA msg.wParam = wp; msg.lParam = lp; QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) qintptr result; -#else - long result; -#endif if (!dispatcher) { if (message == WM_TIMER) KillTimer(hwnd, wp); @@ -163,13 +105,9 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result)) return result; -#ifdef GWLP_USERDATA auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); -#else - auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA)); -#endif - QEventDispatcherWin32Private *d = 0; - if (q != 0) + QEventDispatcherWin32Private *d = nullptr; + if (q != nullptr) d = q->d_func(); switch (message) { @@ -193,11 +131,11 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA break; } if (type >= 0) { - Q_ASSERT(d != 0); + Q_ASSERT(d != nullptr); QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read }; QSNDict *dict = sn_vec[type]; - QSockNot *sn = dict ? dict->value(wp) : 0; + QSockNot *sn = dict ? dict->value(qintptr(wp)) : 0; if (sn == nullptr) { d->postActivateSocketNotifiers(); } else { @@ -223,7 +161,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA return 0; } case WM_QT_ACTIVATENOTIFIERS: { - Q_ASSERT(d != 0); + Q_ASSERT(d != nullptr); // Postpone activation if we have unhandled socket notifier messages // in the queue. WM_QT_ACTIVATENOTIFIERS will be posted again as a result of @@ -248,7 +186,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA return 0; } case WM_TIMER: - Q_ASSERT(d != 0); + Q_ASSERT(d != nullptr); if (wp == d->sendPostedEventsTimerId) q->sendPostedEvents(); @@ -256,39 +194,32 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA d->sendTimerEvent(wp); return 0; case WM_QT_SENDPOSTEDEVENTS: - Q_ASSERT(d != 0); + Q_ASSERT(d != nullptr); // We send posted events manually, if the window procedure was invoked // by the foreign event loop (e.g. from the native modal dialog). // Skip sending, if the message queue is not empty. // sendPostedEventsTimer will deliver posted events later. - static const UINT mask = inputQueueMask(); + static const UINT mask = QS_ALLEVENTS; if (HIWORD(GetQueueStatus(mask)) == 0) q->sendPostedEvents(); + else + d->startPostedEventsTimer(); return 0; } // switch (message) return DefWindowProc(hwnd, message, wp, lp); } -LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp) +void QEventDispatcherWin32Private::startPostedEventsTimer() { - QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance()); - Q_ASSERT(q != 0); - QEventDispatcherWin32Private *d = q->d_func(); - MSG *msg = reinterpret_cast<MSG *>(lp); - // Windows unexpectedly passes PM_NOYIELD flag to the hook procedure, - // if ::PeekMessage(..., PM_REMOVE | PM_NOYIELD) is called from the event loop. - // So, retrieve 'removed' tag as a bit field. - const bool messageRemoved = (wp & PM_REMOVE) != 0; - - if (msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS - && messageRemoved && d->sendPostedEventsTimerId == 0) { + // we received WM_QT_SENDPOSTEDEVENTS, so allow posting it again + wakeUps.storeRelaxed(0); + if (sendPostedEventsTimerId == 0) { // Start a timer to deliver posted events when the message queue is emptied. - d->sendPostedEventsTimerId = SetTimer(d->internalHwnd, SendPostedEventsTimerId, - USER_TIMER_MINIMUM, NULL); + sendPostedEventsTimerId = SetTimer(internalHwnd, SendPostedEventsTimerId, + USER_TIMER_MINIMUM, NULL); } - return d->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0; } // Provide class name and atom for the message window used by @@ -360,24 +291,52 @@ static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatch return 0; } -#ifdef GWLP_USERDATA SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher)); -#else - SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher)); -#endif return wnd; } -static void calculateNextTimeout(WinTimerInfo *t, quint64 currentTime) +static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime) { uint interval = t->interval; - if ((interval >= 20000u && t->timerType != Qt::PreciseTimer) || t->timerType == Qt::VeryCoarseTimer) { - // round the interval, VeryCoarseTimers only have full second accuracy - interval = ((interval + 500)) / 1000 * 1000; + ULONG tolerance = TIMERV_DEFAULT_COALESCING; + switch (t->timerType) { + case Qt::PreciseTimer: + // high precision timer is based on millisecond precision + // so no adjustment is necessary + break; + + case Qt::CoarseTimer: + // this timer has up to 5% coarseness + // so our boundaries are 20 ms and 20 s + // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision + // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer + if (interval >= 20000) { + t->timerType = Qt::VeryCoarseTimer; + } else if (interval <= 20) { + // no adjustment necessary + t->timerType = Qt::PreciseTimer; + break; + } else { + tolerance = interval / 20; + break; + } + Q_FALLTHROUGH(); + case Qt::VeryCoarseTimer: + // the very coarse timer is based on full second precision, + // so we round to closest second (but never to zero) + tolerance = 1000; + if (interval < 1000) + interval = 1000; + else + interval = (interval + 500) / 1000 * 1000; + currentTime = currentTime / 1000 * 1000; + break; } + t->interval = interval; t->timeout = currentTime + interval; + return tolerance; } void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) @@ -387,13 +346,13 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) Q_Q(QEventDispatcherWin32); bool ok = false; - calculateNextTimeout(t, qt_msectime()); + ULONG tolerance = calculateNextTimeout(t, qt_msectime()); uint interval = t->interval; if (interval == 0u) { // optimization for single-shot-zero-timer QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId)); ok = true; - } else if (interval < 20u || t->timerType == Qt::PreciseTimer) { + } else if (tolerance == TIMERV_DEFAULT_COALESCING) { // 3/2016: Although MSDN states timeSetEvent() is deprecated, the function // is still deemed to be the most reliable precision timer. t->fastTimerId = timeSetEvent(interval, 1, qt_fast_timer_proc, DWORD_PTR(t), @@ -403,8 +362,10 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t) if (!ok) { // user normal timers for (Very)CoarseTimers, or if no more multimedia timers available - ok = SetTimer(internalHwnd, t->timerId, interval, 0); + ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance); } + if (!ok) + ok = SetTimer(internalHwnd, t->timerId, interval, nullptr); if (!ok) qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer"); @@ -417,7 +378,7 @@ void QEventDispatcherWin32Private::unregisterTimer(WinTimerInfo *t) } else if (t->fastTimerId != 0) { timeKillEvent(t->fastTimerId); QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId); - } else if (internalHwnd) { + } else { KillTimer(internalHwnd, t->timerId); } t->timerId = -1; @@ -447,7 +408,7 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId) } } -void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket, long event) +void QEventDispatcherWin32Private::doWsaAsyncSelect(qintptr socket, long event) { Q_ASSERT(internalHwnd); // BoundsChecker may emit a warning for WSAAsyncSelect when event == 0 @@ -461,35 +422,18 @@ void QEventDispatcherWin32Private::postActivateSocketNotifiers() activateNotifiersPosted = PostMessage(internalHwnd, WM_QT_ACTIVATENOTIFIERS, 0, 0); } -void QEventDispatcherWin32::createInternalHwnd() -{ - Q_D(QEventDispatcherWin32); - - if (d->internalHwnd) - return; - d->internalHwnd = qt_create_internal_window(this); - - // setup GetMessage hook needed to drive our posted events - d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId()); - if (Q_UNLIKELY(!d->getMessageHook)) { - int errorCode = GetLastError(); - qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls", - errorCode, qUtf16Printable(qt_error_string(errorCode))); - } - - // start all normal timers - for (int i = 0; i < d->timerVec.count(); ++i) - d->registerTimer(d->timerVec.at(i)); -} - QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent) - : QAbstractEventDispatcher(*new QEventDispatcherWin32Private, parent) + : QEventDispatcherWin32(*new QEventDispatcherWin32Private, parent) { } QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent) : QAbstractEventDispatcher(dd, parent) -{ } +{ + Q_D(QEventDispatcherWin32); + + d->internalHwnd = qt_create_internal_window(this); +} QEventDispatcherWin32::~QEventDispatcherWin32() { @@ -515,133 +459,105 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); - if (!d->internalHwnd) { - createInternalHwnd(); - wakeUp(); // trigger a call to sendPostedEvents() - } - - d->interrupt.storeRelaxed(false); + // We don't know _when_ the interrupt occurred so we have to honor it. + const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false); emit awake(); // To prevent livelocks, send posted events once per iteration. // QCoreApplication::sendPostedEvents() takes care about recursions. sendPostedEvents(); + if (wasInterrupted) + return false; + + auto threadData = d->threadData.loadRelaxed(); bool canWait; bool retVal = false; do { - DWORD waitRet = 0; - DWORD nCount = 0; - HANDLE *pHandles = nullptr; - if (d->winEventNotifierActivatedEvent) { - nCount = 1; - pHandles = &d->winEventNotifierActivatedEvent; - } QVarLengthArray<MSG> processedTimers; while (!d->interrupt.loadRelaxed()) { MSG msg; - bool haveMessage; if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { // process queued user input events - haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); - } else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { + } else if (!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events - haveMessage = true; msg = d->queuedSocketEvents.takeFirst(); - } else { - haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE); - if (haveMessage) { - if (flags.testFlag(QEventLoop::ExcludeUserInputEvents) - && isUserInputMessage(msg.message)) { - // queue user input events for later processing - d->queuedUserInputEvents.append(msg); - continue; - } - if ((flags & QEventLoop::ExcludeSocketNotifiers) - && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { - // queue socket events for later processing - d->queuedSocketEvents.append(msg); - continue; - } + } else if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if (flags.testFlag(QEventLoop::ExcludeUserInputEvents) + && isUserInputMessage(msg.message)) { + // queue user input events for later processing + d->queuedUserInputEvents.append(msg); + continue; } - } - if (!haveMessage) { - // no message - check for signalled objects - waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); - if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { - // a new message has arrived, process it + if ((flags & QEventLoop::ExcludeSocketNotifiers) + && (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) { + // queue socket events for later processing + d->queuedSocketEvents.append(msg); continue; } + } else if (MsgWaitForMultipleObjectsEx(0, NULL, 0, QS_ALLINPUT, MWMO_ALERTABLE) + == WAIT_OBJECT_0) { + // a new message has arrived, process it + continue; + } else { + // nothing to do, so break + break; } - if (haveMessage) { - if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { - // Set result to 'true' because the message was sent by wakeUp(). - retVal = true; + + if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) { + d->startPostedEventsTimer(); + // Set result to 'true' because the message was sent by wakeUp(). + retVal = true; + continue; + } + if (msg.message == WM_TIMER) { + // Skip timer event intended for use inside foreign loop. + if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId) continue; - } - if (msg.message == WM_TIMER) { - // Skip timer event intended for use inside foreign loop. - if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId) - continue; - - // avoid live-lock by keeping track of the timers we've already sent - bool found = false; - for (int i = 0; !found && i < processedTimers.count(); ++i) { - const MSG processed = processedTimers.constData()[i]; - found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); - } - if (found) - continue; - processedTimers.append(msg); - } else if (msg.message == WM_QUIT) { - if (QCoreApplication::instance()) - QCoreApplication::instance()->quit(); - return false; - } - if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); + // avoid live-lock by keeping track of the timers we've already sent + bool found = false; + for (int i = 0; !found && i < processedTimers.count(); ++i) { + const MSG processed = processedTimers.constData()[i]; + found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); } - } else if (waitRet - WAIT_OBJECT_0 < nCount) { - activateEventNotifiers(); - } else { - // nothing todo so break - break; + if (found) + continue; + processedTimers.append(msg); + } else if (msg.message == WM_QUIT) { + if (QCoreApplication::instance()) + QCoreApplication::instance()->quit(); + return false; + } + + if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); } retVal = true; } - // still nothing - wait for message or signalled objects + // wait for message canWait = (!retVal && !d->interrupt.loadRelaxed() - && (flags & QEventLoop::WaitForMoreEvents)); + && flags.testFlag(QEventLoop::WaitForMoreEvents) + && threadData->canWaitLocked()); if (canWait) { emit aboutToBlock(); - waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); - if (waitRet - WAIT_OBJECT_0 < nCount) { - activateEventNotifiers(); - retVal = true; - } } } while (canWait); return retVal; } -bool QEventDispatcherWin32::hasPendingEvents() -{ - MSG msg; - return qGlobalPostedEventsCount() || PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); -} - void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); int type = notifier->type(); #ifndef QT_NO_DEBUG if (sockfd < 0) { @@ -665,11 +581,9 @@ void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) const char *t[] = { "Read", "Write", "Exception" }; /* Variable "socket" below is a function pointer. */ qWarning("QSocketNotifier: Multiple socket notifiers for " - "same socket %d and type %s", sockfd, t[type]); + "same socket %" PRIdQINTPTR " and type %s", sockfd, t[type]); } - createInternalHwnd(); - QSockNot *sn = new QSockNot; sn->obj = notifier; sn->fd = sockfd; @@ -692,9 +606,16 @@ void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier) } sd.event |= event; } else { - // Disable the events which could be implicitly re-enabled. Next activation - // of socket notifiers will reset the mask. - d->active_fd.insert(sockfd, QSockFd(event, FD_READ | FD_ACCEPT | FD_WRITE | FD_OOB)); + // Although WSAAsyncSelect(..., 0), which is called from + // unregisterSocketNotifier(), immediately disables event message + // posting for the socket, it is possible that messages could be + // waiting in the application message queue even if the socket was + // closed. Also, some events could be implicitly re-enabled due + // to system calls. Ignore these superfluous events until all + // pending notifications have been suppressed. Next activation of + // socket notifiers will reset the mask. + d->active_fd.insert(sockfd, QSockFd(event, FD_READ | FD_CLOSE | FD_ACCEPT | FD_WRITE + | FD_CONNECT | FD_OOB)); } d->postActivateSocketNotifiers(); @@ -704,7 +625,7 @@ void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier) { Q_ASSERT(notifier); #ifndef QT_NO_DEBUG - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); if (sockfd < 0) { qWarning("QEventDispatcherWin32::unregisterSocketNotifier: invalid socket identifier"); return; @@ -721,7 +642,7 @@ void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier { Q_D(QEventDispatcherWin32); int type = notifier->type(); - int sockfd = notifier->socket(); + qintptr sockfd = notifier->socket(); Q_ASSERT(sockfd >= 0); QSFDict::iterator it = d->active_fd.find(sockfd); @@ -749,7 +670,7 @@ void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier delete sn; } -void QEventDispatcherWin32::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object) +void QEventDispatcherWin32::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) { #ifndef QT_NO_DEBUG if (timerId < 1 || interval < 0 || !object) { @@ -778,10 +699,8 @@ void QEventDispatcherWin32::registerTimer(int timerId, int interval, Qt::TimerTy t->inTimerEvent = false; t->fastTimerId = 0; - if (d->internalHwnd) - d->registerTimer(t); + d->registerTimer(t); - d->timerVec.append(t); // store in timer vector d->timerDict.insert(t->timerId, t); // store timers in dict } @@ -799,15 +718,11 @@ bool QEventDispatcherWin32::unregisterTimer(int timerId) #endif Q_D(QEventDispatcherWin32); - if (d->timerVec.isEmpty() || timerId <= 0) - return false; - WinTimerInfo *t = d->timerDict.value(timerId); + WinTimerInfo *t = d->timerDict.take(timerId); if (!t) return false; - d->timerDict.remove(t->timerId); - d->timerVec.removeAll(t); d->unregisterTimer(t); return true; } @@ -826,16 +741,18 @@ bool QEventDispatcherWin32::unregisterTimers(QObject *object) #endif Q_D(QEventDispatcherWin32); - if (d->timerVec.isEmpty()) + if (d->timerDict.isEmpty()) return false; - WinTimerInfo *t; - for (int i=0; i<d->timerVec.size(); i++) { - t = d->timerVec.at(i); - if (t && t->obj == object) { // object found - d->timerDict.remove(t->timerId); - d->timerVec.removeAt(i); + + auto it = d->timerDict.begin(); + while (it != d->timerDict.end()) { + WinTimerInfo *t = it.value(); + Q_ASSERT(t); + if (t->obj == object) { + it = d->timerDict.erase(it); d->unregisterTimer(t); - --i; + } else { + ++it; } } return true; @@ -853,91 +770,14 @@ QEventDispatcherWin32::registeredTimers(QObject *object) const Q_D(const QEventDispatcherWin32); QList<TimerInfo> list; - for (const WinTimerInfo *t : qAsConst(d->timerVec)) { - if (t && t->obj == object) + for (WinTimerInfo *t : std::as_const(d->timerDict)) { + Q_ASSERT(t); + if (t->obj == object) list << TimerInfo(t->timerId, t->interval, t->timerType); } return list; } -bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier) -{ - Q_ASSERT(notifier); -#ifndef QT_NO_DEBUG - if (notifier->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QEventDispatcherWin32: event notifiers cannot be enabled from another thread"); - return false; - } -#endif - - Q_D(QEventDispatcherWin32); - - if (d->winEventNotifierList.contains(notifier)) - return true; - - d->winEventNotifierList.append(notifier); - d->winEventNotifierListModified = true; - - if (!d->winEventNotifierActivatedEvent) - d->winEventNotifierActivatedEvent = CreateEvent(0, TRUE, FALSE, nullptr); - - return QWinEventNotifierPrivate::get(notifier)->registerWaitObject(); -} - -void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier) -{ - Q_ASSERT(notifier); -#ifndef QT_NO_DEBUG - if (notifier->thread() != thread() || thread() != QThread::currentThread()) { - qWarning("QEventDispatcherWin32: event notifiers cannot be disabled from another thread"); - return; - } -#endif - doUnregisterEventNotifier(notifier); -} - -void QEventDispatcherWin32::doUnregisterEventNotifier(QWinEventNotifier *notifier) -{ - Q_D(QEventDispatcherWin32); - - int i = d->winEventNotifierList.indexOf(notifier); - if (i == -1) - return; - d->winEventNotifierList.takeAt(i); - d->winEventNotifierListModified = true; - QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); - if (nd->waitHandle) - nd->unregisterWaitObject(); -} - -void QEventDispatcherWin32::activateEventNotifiers() -{ - Q_D(QEventDispatcherWin32); - ResetEvent(d->winEventNotifierActivatedEvent); - - // Activate signaled notifiers. Our winEventNotifierList can be modified in activation slots. - do { - d->winEventNotifierListModified = false; - for (int i = 0; i < d->winEventNotifierList.count(); ++i) { - QWinEventNotifier *notifier = d->winEventNotifierList.at(i); - QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); - if (nd->signaledCount.loadRelaxed() != 0) { - --nd->signaledCount; - nd->unregisterWaitObject(); - d->activateEventNotifier(notifier); - } - } - } while (d->winEventNotifierListModified); - - // Re-register the remaining activated notifiers. - for (int i = 0; i < d->winEventNotifierList.count(); ++i) { - QWinEventNotifier *notifier = d->winEventNotifierList.at(i); - QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); - if (!nd->waitHandle) - nd->registerWaitObject(); - } -} - int QEventDispatcherWin32::remainingTime(int timerId) { #ifndef QT_NO_DEBUG @@ -949,20 +789,12 @@ int QEventDispatcherWin32::remainingTime(int timerId) Q_D(QEventDispatcherWin32); - if (d->timerVec.isEmpty()) - return -1; - quint64 currentTime = qt_msectime(); - for (const WinTimerInfo *t : qAsConst(d->timerVec)) { - if (t && t->timerId == timerId) { - // timer found, return time to wait - - if (d->internalHwnd) - return t->timeout > currentTime ? t->timeout - currentTime : 0; - else - return t->interval; - } + WinTimerInfo *t = d->timerDict.value(timerId); + if (t) { + // timer found, return time to wait + return t->timeout > currentTime ? t->timeout - currentTime : 0; } #ifndef QT_NO_DEBUG @@ -975,7 +807,7 @@ int QEventDispatcherWin32::remainingTime(int timerId) void QEventDispatcherWin32::wakeUp() { Q_D(QEventDispatcherWin32); - if (d->internalHwnd && d->wakeUps.testAndSetRelaxed(0, 1)) { + if (d->wakeUps.testAndSetRelaxed(0, 1)) { // post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0)) qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message"); @@ -989,9 +821,6 @@ void QEventDispatcherWin32::interrupt() wakeUp(); } -void QEventDispatcherWin32::flush() -{ } - void QEventDispatcherWin32::startingUp() { } @@ -1008,22 +837,13 @@ void QEventDispatcherWin32::closingDown() doUnregisterSocketNotifier((*(d->sn_except.begin()))->obj); Q_ASSERT(d->active_fd.isEmpty()); - // clean up any eventnotifiers - while (!d->winEventNotifierList.isEmpty()) - doUnregisterEventNotifier(d->winEventNotifierList.first()); - // clean up any timers - for (int i = 0; i < d->timerVec.count(); ++i) - d->unregisterTimer(d->timerVec.at(i)); - d->timerVec.clear(); + for (WinTimerInfo *t : std::as_const(d->timerDict)) + d->unregisterTimer(t); d->timerDict.clear(); d->closingDown = true; - if (d->getMessageHook) - UnhookWindowsHookEx(d->getMessageHook); - d->getMessageHook = 0; - if (d->sendPostedEventsTimerId != 0) KillTimer(d->internalHwnd, d->sendPostedEventsTimerId); d->sendPostedEventsTimerId = 0; @@ -1081,9 +901,9 @@ void QEventDispatcherWin32::sendPostedEvents() HWND QEventDispatcherWin32::internalHwnd() { - Q_D(QEventDispatcherWin32); - createInternalHwnd(); - return d->internalHwnd; + return d_func()->internalHwnd; } QT_END_NAMESPACE + +#include "moc_qeventdispatcher_win_p.cpp" |