From 3691f7ca0c7117f18ea38ca3950ee9a8a91a53c8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 20 Sep 2013 02:38:22 +0200 Subject: QFutureInterface: make accesses to 'state' thread-safe Introduce helper functions switch_{on,off,from_to} to make the code more readable, and prepare everything for later optimizations reducing the sizes of critical sections (by locking the mutex later, or even never). This commit, however, is only concerned with shutting up tsan. In waitForResult(), simplified the code by removing an unneeded if guard: the condition is checked in the while loop immediately following in the then-block, and the local variable declaration that precedes the loop is not worth guarding. Change-Id: I24bfd864ca96f862302536ad8662065e6f366fa8 Reviewed-by: David Faure --- src/corelib/thread/qfutureinterface.cpp | 109 ++++++++++++++++++-------------- src/corelib/thread/qfutureinterface_p.h | 2 +- 2 files changed, 64 insertions(+), 47 deletions(-) (limited to 'src/corelib/thread') diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index 2fe038165b..a29f8ebba7 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -77,13 +77,33 @@ QFutureInterfaceBase::~QFutureInterfaceBase() delete d; } +static inline int switch_on(QAtomicInt &a, int which) +{ + return a.fetchAndOrRelaxed(which) | which; +} + +static inline int switch_off(QAtomicInt &a, int which) +{ + return a.fetchAndAndRelaxed(~which) & ~which; +} + +static inline int switch_from_to(QAtomicInt &a, int from, int to) +{ + int newValue; + int expected = a.load(); + do { + newValue = (expected & ~from) | to; + } while (!a.testAndSetRelaxed(expected, newValue, expected)); + return newValue; +} + void QFutureInterfaceBase::cancel() { QMutexLocker locker(&d->m_mutex); - if (d->state & Canceled) + if (d->state.load() & Canceled) return; - d->state = State((d->state & ~Paused) | Canceled); + switch_from_to(d->state, Paused, Canceled); d->waitCondition.wakeAll(); d->pausedWaitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); @@ -93,10 +113,10 @@ void QFutureInterfaceBase::setPaused(bool paused) { QMutexLocker locker(&d->m_mutex); if (paused) { - d->state = State(d->state | Paused); + switch_on(d->state, Paused); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); } else { - d->state = State(d->state & ~Paused); + switch_off(d->state, Paused); d->pausedWaitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed)); } @@ -105,29 +125,24 @@ void QFutureInterfaceBase::setPaused(bool paused) void QFutureInterfaceBase::togglePaused() { QMutexLocker locker(&d->m_mutex); - if (d->state & Paused) { - d->state = State(d->state & ~Paused); + if (d->state.load() & Paused) { + switch_off(d->state, Paused); d->pausedWaitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Resumed)); } else { - d->state = State(d->state | Paused); + switch_on(d->state, Paused); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); } } void QFutureInterfaceBase::setThrottled(bool enable) { - // bail out if we are not changing the state - if ((enable && (d->state & Throttled)) || (!enable && !(d->state & Throttled))) - return; - - // lock and change the state QMutexLocker lock(&d->m_mutex); if (enable) { - d->state = State(d->state | Throttled); + switch_on(d->state, Throttled); } else { - d->state = State(d->state & ~Throttled); - if (!(d->state & Paused)) + switch_off(d->state, Throttled); + if (!(d->state.load() & Paused)) d->pausedWaitCondition.wakeAll(); } } @@ -178,11 +193,15 @@ bool QFutureInterfaceBase::waitForNextResult() void QFutureInterfaceBase::waitForResume() { // return early if possible to avoid taking the mutex lock. - if ((d->state & Paused) == false || (d->state & Canceled)) - return; + { + const int state = d->state.load(); + if (!(state & Paused) || (state & Canceled)) + return; + } QMutexLocker lock(&d->m_mutex); - if ((d->state & Paused) == false || (d->state & Canceled)) + const int state = d->state.load(); + if (!(state & Paused) || (state & Canceled)) return; // decrease active thread count since this thread will wait. @@ -230,7 +249,7 @@ bool QFutureInterfaceBase::isProgressUpdateNeeded() const void QFutureInterfaceBase::reportStarted() { QMutexLocker locker(&d->m_mutex); - if ((d->state & Started) || (d->state & Canceled) || (d->state & Finished)) + if (d->state.load() & (Started|Canceled|Finished)) return; d->setState(State(Started | Running)); @@ -246,11 +265,11 @@ void QFutureInterfaceBase::reportCanceled() void QFutureInterfaceBase::reportException(const QException &exception) { QMutexLocker locker(&d->m_mutex); - if ((d->state & Canceled) || (d->state & Finished)) + if (d->state.load() & (Canceled|Finished)) return; d->m_exceptionStore.setException(exception); - d->state = State(d->state | Canceled); + switch_on(d->state, Canceled); d->waitCondition.wakeAll(); d->pausedWaitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); @@ -260,8 +279,8 @@ void QFutureInterfaceBase::reportException(const QException &exception) void QFutureInterfaceBase::reportFinished() { QMutexLocker locker(&d->m_mutex); - if (!(d->state & Finished)) { - d->state = State((d->state & ~Running) | Finished); + if (!isFinished()) { + switch_from_to(d->state, Running, Finished); d->waitCondition.wakeAll(); d->sendCallOut(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); } @@ -281,7 +300,7 @@ int QFutureInterfaceBase::expectedResultCount() bool QFutureInterfaceBase::queryState(State state) const { - return (d->state & state); + return d->state.load() & state; } void QFutureInterfaceBase::waitForResult(int resultIndex) @@ -289,7 +308,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex) d->m_exceptionStore.throwPossibleException(); QMutexLocker lock(&d->m_mutex); - if (!(d->state & Running)) + if (!isRunning()) return; lock.unlock(); @@ -299,11 +318,9 @@ void QFutureInterfaceBase::waitForResult(int resultIndex) lock.relock(); - if (d->state & Running) { - const int waitIndex = (resultIndex == -1) ? INT_MAX : resultIndex; - while ((d->state & Running) && d->internal_isResultReadyAt(waitIndex) == false) - d->waitCondition.wait(&d->m_mutex); - } + const int waitIndex = (resultIndex == -1) ? INT_MAX : resultIndex; + while (isRunning() && !d->internal_isResultReadyAt(waitIndex)) + d->waitCondition.wait(&d->m_mutex); d->m_exceptionStore.throwPossibleException(); } @@ -311,7 +328,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex) void QFutureInterfaceBase::waitForFinished() { QMutexLocker lock(&d->m_mutex); - const bool alreadyFinished = !(d->state & Running); + const bool alreadyFinished = !isRunning(); lock.unlock(); if (!alreadyFinished) { @@ -319,7 +336,7 @@ void QFutureInterfaceBase::waitForFinished() lock.relock(); - while (d->state & Running) + while (isRunning()) d->waitCondition.wait(&d->m_mutex); } @@ -328,7 +345,7 @@ void QFutureInterfaceBase::waitForFinished() void QFutureInterfaceBase::reportResultsReady(int beginIndex, int endIndex) { - if ((d->state & Canceled) || (d->state & Finished) || beginIndex == endIndex) + if (beginIndex == endIndex || (d->state.load() & (Canceled|Finished))) return; d->waitCondition.wakeAll(); @@ -390,7 +407,7 @@ void QFutureInterfaceBase::setProgressValueAndText(int progressValue, if (d->m_progressValue >= progressValue) return; - if ((d->state & Canceled) || (d->state & Finished)) + if (d->state.load() & (Canceled|Finished)) return; if (d->internal_updateProgress(progressValue, progressText)) { @@ -462,10 +479,10 @@ bool QFutureInterfaceBasePrivate::internal_waitForNextResult() if (m_results.hasNextResult()) return true; - while ((state & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) + while ((state.load() & QFutureInterfaceBase::Running) && m_results.hasNextResult() == false) waitCondition.wait(&m_mutex); - return (!(state & QFutureInterfaceBase::Canceled) && m_results.hasNextResult()); + return !(state.load() & QFutureInterfaceBase::Canceled) && m_results.hasNextResult(); } bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, @@ -488,16 +505,16 @@ bool QFutureInterfaceBasePrivate::internal_updateProgress(int progress, void QFutureInterfaceBasePrivate::internal_setThrottled(bool enable) { // bail out if we are not changing the state - if ((enable && (state & QFutureInterfaceBase::Throttled)) - || (!enable && !(state & QFutureInterfaceBase::Throttled))) + if ((enable && (state.load() & QFutureInterfaceBase::Throttled)) + || (!enable && !(state.load() & QFutureInterfaceBase::Throttled))) return; // change the state if (enable) { - state = QFutureInterfaceBase::State(state | QFutureInterfaceBase::Throttled); + switch_on(state, QFutureInterfaceBase::Throttled); } else { - state = QFutureInterfaceBase::State(state & ~QFutureInterfaceBase::Throttled); - if (!(state & QFutureInterfaceBase::Paused)) + switch_off(state, QFutureInterfaceBase::Throttled); + if (!(state.load() & QFutureInterfaceBase::Paused)) pausedWaitCondition.wakeAll(); } } @@ -532,7 +549,7 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface { QMutexLocker locker(&m_mutex); - if (state & QFutureInterfaceBase::Started) { + if (state.load() & QFutureInterfaceBase::Started) { interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Started)); interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::ProgressRange, m_progressMinimum, @@ -552,13 +569,13 @@ void QFutureInterfaceBasePrivate::connectOutputInterface(QFutureCallOutInterface it.batchedAdvance(); } - if (state & QFutureInterfaceBase::Paused) + if (state.load() & QFutureInterfaceBase::Paused) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Paused)); - if (state & QFutureInterfaceBase::Canceled) + if (state.load() & QFutureInterfaceBase::Canceled) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Canceled)); - if (state & QFutureInterfaceBase::Finished) + if (state.load() & QFutureInterfaceBase::Finished) interface->postCallOutEvent(QFutureCallOutEvent(QFutureCallOutEvent::Finished)); outputConnections.append(interface); @@ -577,7 +594,7 @@ void QFutureInterfaceBasePrivate::disconnectOutputInterface(QFutureCallOutInterf void QFutureInterfaceBasePrivate::setState(QFutureInterfaceBase::State newState) { - state = newState; + state.store(newState); } QT_END_NAMESPACE diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h index e2d588cd07..eb0c38421b 100644 --- a/src/corelib/thread/qfutureinterface_p.h +++ b/src/corelib/thread/qfutureinterface_p.h @@ -155,7 +155,7 @@ public: int m_progressValue; // TQ int m_progressMinimum; // TQ int m_progressMaximum; // TQ - QFutureInterfaceBase::State state; + QAtomicInt state; // reads and writes can happen unprotected, both must be atomic QElapsedTimer progressTime; QWaitCondition pausedWaitCondition; QtPrivate::ResultStoreBase m_results; -- cgit v1.2.3 From 9f17c245893e296d5c92ed1ef1e5d08896b469b1 Mon Sep 17 00:00:00 2001 From: Oliver Wolff Date: Thu, 17 Nov 2016 10:40:44 +0100 Subject: Enable constexpr support for Visual Studio 2017 Change-Id: I894789c41cc2c1a327c14d0526e658520d096085 Reviewed-by: Friedemann Kleint Reviewed-by: Thiago Macieira --- src/corelib/thread/qbasicatomic.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/corelib/thread') diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index 7960174277..c1e01a3212 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -78,6 +78,9 @@ # error "Qt has not been ported to this platform" #endif +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4522) + QT_BEGIN_NAMESPACE #if 0 @@ -340,4 +343,6 @@ public: QT_END_NAMESPACE +QT_WARNING_POP + #endif // QBASICATOMIC_H -- cgit v1.2.3