summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2019-12-04 14:58:27 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2019-12-10 11:33:26 +0100
commit30e32870a052fda1a23099e46864242e52d94a17 (patch)
tree3114b7167b40cd4500c633d52e0b4b48452d16cc /src/plugins
parent95689c645fb42260d920091658b2d39c920abf00 (diff)
QCocoaEventDispatcher: make 'interrupt' work
even if we are currently inside processEvents (apparently called manually and not from QEventLoop::exec()). A carefully crafted application (see, for example, the linked QTBUG or even updated auto-test) can trigger itself into failing to exit the current (potentially nested) event loop. We can harden our Cocoa event dispatcher to detect such condition and properly propagate 'interrupt' to where it'll do its job, indeed, interrupting the real event loop (aka [NSApp run]). This mainly means we have to undo what bool blocker would erroneously do. Also, long live (as people love to say these days) to another tricky (somewhat) auto-test (surely, it's not flaky!). Fixes: QTBUG-79477 Change-Id: I794f0cda23e24d36be67f2bb63d52b74be057c31 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm22
2 files changed, 21 insertions, 2 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
index 69587a24be..b8d2532b8e 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.h
@@ -186,6 +186,7 @@ public:
QAtomicInt serialNumber;
int lastSerial;
bool interrupt;
+ bool propagateInterrupt = false;
static void postedEventsSourceCallback(void *info);
static void waitingObserverCallback(CFRunLoopObserverRef observer,
diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
index 110c82bf1f..94b9e62eab 100644
--- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
+++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm
@@ -84,13 +84,12 @@
#include "private/qthread_p.h"
#include "private/qguiapplication_p.h"
#include <qdebug.h>
+#include <qscopeguard.h>
#include <AppKit/AppKit.h>
QT_BEGIN_NAMESPACE
-QT_USE_NAMESPACE
-
static inline CFRunLoopRef mainRunLoop()
{
return CFRunLoopGetMain();
@@ -348,6 +347,16 @@ static inline void qt_mac_waitForMoreEvents(NSString *runLoopMode = NSDefaultRun
bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QCocoaEventDispatcher);
+
+ // In rare rather corner cases a user's application messes with
+ // QEventLoop::exec()/exit() and QCoreApplication::processEvents(),
+ // we have to undo what bool blocker normally does.
+ d->propagateInterrupt = false;
+ const auto boolBlockerUndo = qScopeGuard([d](){
+ if (d->propagateInterrupt)
+ d->interrupt = true;
+ d->propagateInterrupt = false;
+ });
QBoolBlocker interruptBlocker(d->interrupt, false);
bool interruptLater = false;
@@ -496,7 +505,16 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
if ((d->processEventsFlags & QEventLoop::EventLoopExec) == 0) {
// When called "manually", always process posted events and timers
+ bool oldInterrupt = d->interrupt;
d->processPostedEvents();
+ if (!oldInterrupt && d->interrupt && !d->currentModalSession()) {
+ // We had direct processEvent call, coming not from QEventLoop::exec().
+ // One of the posted events triggered an application to interrupt the loop.
+ // But bool blocker will reset d->interrupt to false, so the real event
+ // loop will never notice it was interrupted. Now we'll have to fix it by
+ // enforcing the value of d->interrupt.
+ d->propagateInterrupt = true;
+ }
retVal = d->processTimers() || retVal;
}