diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index b6947dc7dc..358a59d05b 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -460,30 +460,6 @@ bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, vo return false; } -/*! \fn bool QAbstractEventDispatcher::registerEventNotifier(QWinEventNotifier *notifier) - - This pure virtual method exists on windows only and has to be reimplemented by a Windows specific - event dispatcher implementation. \a notifier is the QWinEventNotifier instance to be registered. - - The method should return true if the registration of \a notifier was successful, otherwise false. - - QWinEventNotifier calls this method in it's constructor and there should never be a need to call this - method directly. - - \sa QWinEventNotifier, unregisterEventNotifier() -*/ - -/*! \fn bool QAbstractEventDispatcher::unregisterEventNotifier(QWinEventNotifier *notifier) - - This pure virtual method exists on windows only and has to be reimplemented by a Windows specific - event dispatcher implementation. \a notifier is the QWinEventNotifier instance to be unregistered. - - QWinEventNotifier calls this method in it's destructor and there should never be a need to call this - method directly. - - \sa QWinEventNotifier, registerEventNotifier() -*/ - /*! \fn void QAbstractEventDispatcher::awake() This signal is emitted after the event loop returns from a diff --git a/src/corelib/kernel/qabstracteventdispatcher.h b/src/corelib/kernel/qabstracteventdispatcher.h index 9a0a049ed4..717fdc8984 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.h +++ b/src/corelib/kernel/qabstracteventdispatcher.h @@ -49,10 +49,6 @@ class QAbstractNativeEventFilter; class QAbstractEventDispatcherPrivate; class QSocketNotifier; -#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) -class QWinEventNotifier; -#endif - class Q_CORE_EXPORT QAbstractEventDispatcher : public QObject { Q_OBJECT @@ -88,11 +84,6 @@ public: virtual int remainingTime(int timerId) = 0; -#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) - virtual bool registerEventNotifier(QWinEventNotifier *notifier) = 0; - virtual void unregisterEventNotifier(QWinEventNotifier *notifier) = 0; -#endif - virtual void wakeUp() = 0; virtual void interrupt() = 0; diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 44487d57e2..1336238795 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -47,12 +47,10 @@ #include "qset.h" #include "qsocketnotifier.h" #include "qvarlengtharray.h" -#include "qwineventnotifier.h" #include "qelapsedtimer.h" #include "qcoreapplication_p.h" #include -#include QT_BEGIN_NAMESPACE @@ -99,7 +97,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QEventDispatcherWin32Private::QEventDispatcherWin32Private() : interrupt(false), internalHwnd(0), getMessageHook(0), sendPostedEventsTimerId(0), wakeUps(0), - activateNotifiersPosted(false), activateEventNotifiersPosted(false) + activateNotifiersPosted(false) { } @@ -109,12 +107,6 @@ QEventDispatcherWin32Private::~QEventDispatcherWin32Private() 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*/) { @@ -455,14 +447,6 @@ void QEventDispatcherWin32Private::postActivateSocketNotifiers() activateNotifiersPosted = PostMessage(internalHwnd, WM_QT_ACTIVATENOTIFIERS, 0, 0); } -void QEventDispatcherWin32Private::postActivateEventNotifiers() -{ - Q_Q(QEventDispatcherWin32); - - if (!activateEventNotifiersPosted.fetchAndStoreRelease(true)) - QCoreApplication::postEvent(q, new QEvent(QEvent::WinEventAct)); -} - QEventDispatcherWin32::QEventDispatcherWin32(QObject *parent) : QEventDispatcherWin32(*new QEventDispatcherWin32Private, parent) { @@ -817,83 +801,6 @@ QEventDispatcherWin32::registeredTimers(QObject *object) const 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; - - 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); - - // Enable WM_QT_ACTIVATEWINEVENTS posting. - d->activateEventNotifiersPosted.fetchAndStoreAcquire(false); - - // 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->signaled.loadRelaxed()) { - nd->signaled.storeRelaxed(false); - 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 @@ -958,10 +865,6 @@ 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)); @@ -1009,9 +912,6 @@ bool QEventDispatcherWin32::event(QEvent *e) case QEvent::Timer: d->sendTimerEvent(static_cast(e)->timerId()); break; - case QEvent::WinEventAct: - activateEventNotifiers(); - break; default: break; } diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index a5c063e062..4637185563 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -60,7 +60,6 @@ QT_BEGIN_NAMESPACE -class QWinEventNotifier; class QEventDispatcherWin32Private; // forward declaration @@ -86,10 +85,6 @@ public: bool unregisterTimers(QObject *object) override; QList registeredTimers(QObject *object) const override; - bool registerEventNotifier(QWinEventNotifier *notifier) override; - void unregisterEventNotifier(QWinEventNotifier *notifier) override; - void activateEventNotifiers(); - int remainingTime(int timerId) override; void wakeUp() override; @@ -106,7 +101,6 @@ protected: QEventDispatcherWin32(QEventDispatcherWin32Private &dd, QObject *parent = nullptr); virtual void sendPostedEvents(); void doUnregisterSocketNotifier(QSocketNotifier *notifier); - void doUnregisterEventNotifier(QWinEventNotifier *notifier); private: friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp); @@ -156,7 +150,6 @@ class Q_CORE_EXPORT QEventDispatcherWin32Private : public QAbstractEventDispatch public: QEventDispatcherWin32Private(); ~QEventDispatcherWin32Private(); - static QEventDispatcherWin32Private *get(QEventDispatcherWin32 *q) { return q->d_func(); } QAtomicInt interrupt; @@ -186,12 +179,6 @@ public: bool closingDown = false; - bool winEventNotifierListModified = false; - QAtomicInt activateEventNotifiersPosted; - QList winEventNotifierList; - void postActivateEventNotifiers(); - void activateEventNotifier(QWinEventNotifier * wen); - QList queuedUserInputEvents; QList queuedSocketEvents; }; diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp index 5a3ad9ed5d..d3be82db58 100644 --- a/src/corelib/kernel/qwineventnotifier.cpp +++ b/src/corelib/kernel/qwineventnotifier.cpp @@ -39,10 +39,8 @@ #include "qwineventnotifier_p.h" -#include "qeventdispatcher_win_p.h" #include "qcoreapplication.h" - -#include +#include "qthread.h" QT_BEGIN_NAMESPACE @@ -120,12 +118,8 @@ QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent) : QObject(*new QWinEventNotifierPrivate(hEvent, false), parent) { Q_D(QWinEventNotifier); - QAbstractEventDispatcher *eventDispatcher = d->threadData.loadRelaxed()->eventDispatcher.loadRelaxed(); - if (Q_UNLIKELY(!eventDispatcher)) { - qWarning("QWinEventNotifier: Can only be used with threads started with QThread"); - return; - } - eventDispatcher->registerEventNotifier(this); + + d->registerWaitObject(); d->enabled = true; } @@ -193,19 +187,20 @@ void QWinEventNotifier::setEnabled(bool enable) return; d->enabled = enable; - QAbstractEventDispatcher *eventDispatcher = d->threadData.loadRelaxed()->eventDispatcher.loadRelaxed(); - if (!eventDispatcher) // perhaps application is shutting down - return; if (Q_UNLIKELY(thread() != QThread::currentThread())) { qWarning("QWinEventNotifier: Event notifiers cannot be enabled or disabled from another thread"); return; } if (enable) { - d->signaled.storeRelaxed(false); - eventDispatcher->registerEventNotifier(this); - } else { - eventDispatcher->unregisterEventNotifier(this); + // It is possible that the notifier was disabled after an event was already + // posted. In that case we set a state that indicates that such an obsolete + // event shall be ignored. + d->winEventActPosted.testAndSetRelaxed(QWinEventNotifierPrivate::Posted, + QWinEventNotifierPrivate::IgnorePosted); + d->registerWaitObject(); + } else if (d->waitHandle != NULL) { + d->unregisterWaitObject(); } } @@ -225,28 +220,33 @@ bool QWinEventNotifier::event(QEvent * e) } QObject::event(e); // will activate filters if (e->type() == QEvent::WinEventAct) { - emit activated(d->handleToEvent, QPrivateSignal()); + // Emit notification, but only if the event has not been invalidated + // since by the notifier being disabled, even if it was re-enabled + // again. + if (d->winEventActPosted.fetchAndStoreRelaxed(QWinEventNotifierPrivate::NotPosted) + == QWinEventNotifierPrivate::Posted && d->enabled) { + d->unregisterWaitObject(); + + emit activated(d->handleToEvent, QPrivateSignal()); + + if (d->enabled && d->waitHandle == NULL) + d->registerWaitObject(); + } return true; } return false; } -static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/) +void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ignore*/) { QWinEventNotifierPrivate *nd = reinterpret_cast(context); - QAbstractEventDispatcher *eventDispatcher = nd->threadData.loadRelaxed()->eventDispatcher.loadRelaxed(); - // Happens when Q(Core)Application is destroyed before QWinEventNotifier. - // https://bugreports.qt.io/browse/QTBUG-70214 - if (!eventDispatcher) { // perhaps application is shutting down - qWarning("QWinEventNotifier: no event dispatcher, application shutting down? Cannot deliver event."); - return; + // Do not post an event, if an event is already in the message queue. Note + // that an event that was previously invalidated will be reactivated. + if (nd->winEventActPosted.fetchAndStoreRelaxed(QWinEventNotifierPrivate::Posted) + == QWinEventNotifierPrivate::NotPosted) { + QCoreApplication::postEvent(nd->q_func(), new QEvent(QEvent::WinEventAct)); } - - QEventDispatcherWin32Private *edp = QEventDispatcherWin32Private::get( - static_cast(eventDispatcher)); - nd->signaled.storeRelaxed(true); - edp->postActivateEventNotifiers(); } bool QWinEventNotifierPrivate::registerWaitObject() diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h index f8f7cd8eff..da9d8ff9a0 100644 --- a/src/corelib/kernel/qwineventnotifier_p.h +++ b/src/corelib/kernel/qwineventnotifier_p.h @@ -68,13 +68,15 @@ public: QWinEventNotifierPrivate(HANDLE h, bool e) : handleToEvent(h), enabled(e) {} - static QWinEventNotifierPrivate *get(QWinEventNotifier *q) { return q->d_func(); } + static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/); bool registerWaitObject(); void unregisterWaitObject(); HANDLE handleToEvent; HANDLE waitHandle = NULL; - QAtomicInt signaled; + + enum PostingState { NotPosted = 0, Posted, IgnorePosted }; + QAtomicInt winEventActPosted; bool enabled; }; diff --git a/tests/benchmarks/corelib/kernel/CMakeLists.txt b/tests/benchmarks/corelib/kernel/CMakeLists.txt index 2b41ea30f7..556785fd62 100644 --- a/tests/benchmarks/corelib/kernel/CMakeLists.txt +++ b/tests/benchmarks/corelib/kernel/CMakeLists.txt @@ -9,3 +9,6 @@ if(TARGET Qt::Widgets) add_subdirectory(qmetaobject) add_subdirectory(qobject) endif() +if(win32_x_) + add_subdirectory(qwineventnotifier) +endif() diff --git a/tests/benchmarks/corelib/kernel/kernel.pro b/tests/benchmarks/corelib/kernel/kernel.pro index 92f7174419..b7cb23aad6 100644 --- a/tests/benchmarks/corelib/kernel/kernel.pro +++ b/tests/benchmarks/corelib/kernel/kernel.pro @@ -6,8 +6,12 @@ SUBDIRS = \ qobject \ qvariant \ qcoreapplication \ - qtimer_vs_qmetaobject + qtimer_vs_qmetaobject \ + qwineventnotifier !qtHaveModule(widgets): SUBDIRS -= \ qmetaobject \ qobject + +# This test is only applicable on Windows +!win32: SUBDIRS -= qwineventnotifier diff --git a/tests/benchmarks/corelib/kernel/qwineventnotifier/CMakeLists.txt b/tests/benchmarks/corelib/kernel/qwineventnotifier/CMakeLists.txt new file mode 100644 index 0000000000..a2bf2e9574 --- /dev/null +++ b/tests/benchmarks/corelib/kernel/qwineventnotifier/CMakeLists.txt @@ -0,0 +1,15 @@ +# Generated from qwineventnotifier.pro. + +##################################################################### +## tst_bench_qwineventnotifier Binary: +##################################################################### + +qt_add_benchmark(tst_bench_qwineventnotifier + SOURCES + main.cpp + PUBLIC_LIBRARIES + Qt::Test +) + +#### Keys ignored in scope 1:.:.:qwineventnotifier.pro:: +# TEMPLATE = "app" diff --git a/tests/benchmarks/corelib/kernel/qwineventnotifier/main.cpp b/tests/benchmarks/corelib/kernel/qwineventnotifier/main.cpp new file mode 100644 index 0000000000..5ee59f8fb8 --- /dev/null +++ b/tests/benchmarks/corelib/kernel/qwineventnotifier/main.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +class QWinEventNotifierBenchmark : public QObject +{ + Q_OBJECT + +private slots: + void waves_data(); + void waves(); +}; + +class EventsFactory : public QObject +{ + Q_OBJECT + +public: + explicit EventsFactory(int waves, int notifiers, int iterations) + : numberOfWaves(waves), numberOfNotifiers(notifiers), + numberOfIterations(iterations) + { + events.resize(notifiers); + for (int i = 0; i < notifiers; ++i) { + events[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + QVERIFY(events[i] != NULL); + QWinEventNotifier *notifier = new QWinEventNotifier(events[i], this); + Q_CHECK_PTR(notifier); + + connect(notifier, &QWinEventNotifier::activated, [i, this]() { + ResetEvent(this->events[i]); + if (--this->numberOfIterations == 0) + this->eventLoop.quit(); + else + SetEvent(this->events[(i + 1) % this->numberOfNotifiers]); + }); + + connect(this, &EventsFactory::stop, [notifier]() { + notifier->setEnabled(false); + }); + } + } + virtual ~EventsFactory() + { + for (auto event : events) + CloseHandle(event); + } + + void run() + { + Q_ASSERT(numberOfWaves != 0); + + int offset = 0; + for (int i = 0; i < numberOfWaves; ++i) { + SetEvent(events[offset]); + offset += qMax(1, numberOfNotifiers / numberOfWaves); + offset %= numberOfNotifiers; + } + eventLoop.exec(); + } + +signals: + void stop(); + +protected: + QVector events; + QEventLoop eventLoop; + int numberOfWaves; + int numberOfNotifiers; + int numberOfIterations; +}; + +void QWinEventNotifierBenchmark::waves_data() +{ + QTest::addColumn("waves"); + QTest::addColumn("notifiers"); + for (int waves : {1, 3, 10}) { + for (int notifiers : {10, 100, 1000}) + QTest::addRow("waves: %d, notifiers: %d", waves, notifiers) << waves << notifiers; + } +} + +void QWinEventNotifierBenchmark::waves() +{ + QFETCH(int, waves); + QFETCH(int, notifiers); + + const int iterations = 100000; + + EventsFactory factory(waves, notifiers, iterations); + + QElapsedTimer timer; + timer.start(); + + factory.run(); + + qDebug("Elapsed time: %.1f s", timer.elapsed() / 1000.0); + + emit factory.stop(); +} + +QTEST_MAIN(QWinEventNotifierBenchmark) + +#include "main.moc" diff --git a/tests/benchmarks/corelib/kernel/qwineventnotifier/qwineventnotifier.pro b/tests/benchmarks/corelib/kernel/qwineventnotifier/qwineventnotifier.pro new file mode 100644 index 0000000000..5c70ff6cc7 --- /dev/null +++ b/tests/benchmarks/corelib/kernel/qwineventnotifier/qwineventnotifier.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += benchmark +QT = core testlib + +TARGET = tst_bench_qwineventnotifier +SOURCES += main.cpp