diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2019-10-11 00:42:08 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2019-11-13 16:22:40 +0100 |
commit | 782df5b41dd3ab098fd1d3233339079487e1812f (patch) | |
tree | a2727ed7dda4ecff4331e99d05f085bb7fc01184 /src/plugins | |
parent | 4e0d5498eb7ba401e6697182ce74b34d439ecf76 (diff) |
Make QObjectPrivate::threadData a proper atomic
QObjectPrivate::threadData used to be a QThreadData *, and was
read and written from multiple threads without proper synchronization.
As an example, it was read from QCoreApplication::postEvent and
written from QObject::moveToThread, therefore causing UB.
Port threadData to a proper atomic, removing the races. Fix all usage
points.
In general, QObject is documented to be simply reentrant,
not thread-safe, and certain bits (e.g. timers, moveToThread)
are not even reentrant. The reasoning therefore is that a given
QObject's threadData is not supposed to be touched by multiple
threads without some synchronization happening elsewhere, and
therefore relaxed loads should be sufficient.
As drive-by change: refactor QCoreApplication::postEvent.
It was particularly subtle, because it had a loop using a volatile
to cope with the possibility of the receiver object switching thread
while we tried to lock its thread's event queue.
However, volatile does not achieve any synchronization, so drop it,
and refactor the algorithm using better locking primitives.
Put this algorithm in a common place, and also reuse it from
removePostedEvents, which was lacking any synchronization.
Change-Id: Icc755f7eb418ff54b33db4bdd87fd8eaf4e82c7a
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm | 4 |
2 files changed, 4 insertions, 4 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index 9b0a6b1b86..f1fe2aa98b 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -149,7 +149,7 @@ QT_USE_NAMESPACE if ([reflectionDelegate respondsToSelector:_cmd]) return [reflectionDelegate applicationShouldTerminate:sender]; - if (QGuiApplicationPrivate::instance()->threadData->eventLoops.isEmpty()) { + if (QGuiApplicationPrivate::instance()->threadData.loadRelaxed()->eventLoops.isEmpty()) { // No event loop is executing. This probably means that Qt is used as a plugin, // or as a part of a native Cocoa application. In any case it should be fine to // terminate now. @@ -359,7 +359,7 @@ QT_USE_NAMESPACE if (!platformItem || platformItem->menu()) return; - QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData); + QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData.loadRelaxed()); QGuiApplicationPrivate::modifier_buttons = [QNSView convertKeyModifiers:[NSEvent modifierFlags]]; static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated); diff --git a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm index b3ce9e45dc..110c82bf1f 100644 --- a/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm +++ b/src/plugins/platforms/cocoa/qcocoaeventdispatcher.mm @@ -511,7 +511,7 @@ bool QCocoaEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags) if (hadModalSession && !d->currentModalSessionCached) interruptLater = true; } - bool canWait = (d->threadData->canWait + bool canWait = (d->threadData.loadRelaxed()->canWait && !retVal && !d->interrupt && (d->processEventsFlags & QEventLoop::WaitForMoreEvents)); @@ -878,7 +878,7 @@ void QCocoaEventDispatcherPrivate::processPostedEvents() } int serial = serialNumber.loadRelaxed(); - if (!threadData->canWait || (serial != lastSerial)) { + if (!threadData.loadRelaxed()->canWait || (serial != lastSerial)) { lastSerial = serial; QCoreApplication::sendPostedEvents(); QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents); |