diff options
Diffstat (limited to 'src/corelib/thread')
25 files changed, 216 insertions, 108 deletions
diff --git a/src/corelib/thread/qatomic.cpp b/src/corelib/thread/qatomic.cpp index 0184774b44..210218d72d 100644 --- a/src/corelib/thread/qatomic.cpp +++ b/src/corelib/thread/qatomic.cpp @@ -397,14 +397,21 @@ Atomic test-and-set. + \note If you use this function in a loop, consider using the overload with the + additional \c{T ¤tValue} argument instead, which avoids the extra load() on + failure. + If the current value of this QAtomicInteger is the \a expectedValue, the test-and-set functions assign the \a newValue to this QAtomicInteger and return true. If the values are \e not the same, this function does nothing and returns \c false. +//![memory-order-relaxed] This function uses \e relaxed \l {QAtomicInteger#Memory ordering}{memory ordering} semantics, leaving the compiler and processor to freely reorder memory accesses. +//![memory-order-relaxed] + */ /*! @@ -412,15 +419,21 @@ Atomic test-and-set. + \note If you use this function in a loop, consider using the overload with the + additional \c{T ¤tValue} argument instead, which avoids the extra load() on + failure. + If the current value of this QAtomicInteger is the \a expectedValue, the test-and-set functions assign the \a newValue to this QAtomicInteger and return true. If the values are \e not the same, this function does nothing and returns \c false. +//![memory-order-acquire] This function uses \e acquire \l {QAtomicInteger#Memory ordering}{memory ordering} semantics, which ensures that memory access following the atomic operation (in program order) may not be re-ordered before the atomic operation. +//![memory-order-acquire] */ /*! @@ -428,15 +441,21 @@ Atomic test-and-set. + \note If you use this function in a loop, consider using the overload with the + additional \c{T ¤tValue} argument instead, which avoids the extra load() on + failure. + If the current value of this QAtomicInteger is the \a expectedValue, the test-and-set functions assign the \a newValue to this QAtomicInteger and return true. If the values are \e not the same, this function does nothing and returns \c false. +//![memory-order-release] This function uses \e release \l {QAtomicInteger#Memory ordering}{memory ordering} semantics, which ensures that memory access before the atomic operation (in program order) may not be re-ordered after the atomic operation. +//![memory-order-release] */ /*! @@ -444,15 +463,78 @@ Atomic test-and-set. + \note If you use this function in a loop, consider using the overload with the + additional \c{T ¤tValue} argument instead, which avoids the extra load() on + failure. + If the current value of this QAtomicInteger is the \a expectedValue, the test-and-set functions assign the \a newValue to this QAtomicInteger and return true. If the values are \e not the same, this function does nothing and returns \c false. +//![memory-order-ordered] This function uses \e ordered \l {QAtomicInteger#Memory ordering}{memory ordering} semantics, which ensures that memory access before and after the atomic operation (in program order) may not be re-ordered. +//![memory-order-ordered] + +*/ + +/*! + \fn template <typename T> bool QAtomicInteger<T>::testAndSetRelaxed(T expectedValue, T newValue, T ¤tValue) + \since 5.3 + + Atomic test-and-set. + + If the current value of this QAtomicInteger is the \a expectedValue, the + test-and-set functions assign the \a newValue to this QAtomicInteger and + return \c true. If the values are \e not the same, the functions load the + current value of this QAtomicInteger into \a currentValue and return \c false. + + \include qatomic.cpp memory-order-relaxed +*/ + +/*! + \fn template <typename T> bool QAtomicInteger<T>::testAndSetAcquire(T expectedValue, T newValue, T ¤tValue) + \since 5.3 + + Atomic test-and-set. + + If the current value of this QAtomicInteger is the \a expectedValue, the + test-and-set functions assign the \a newValue to this QAtomicInteger and + return \c true. If the values are \e not the same, the functions load the + current value of this QAtomicInteger into \a currentValue and return \c false. + + \include qatomic.cpp memory-order-acquire +*/ + +/*! + \fn template <typename T> bool QAtomicInteger<T>::testAndSetRelease(T expectedValue, T newValue, T ¤tValue) + \since 5.3 + + Atomic test-and-set. + + If the current value of this QAtomicInteger is the \a expectedValue, the + test-and-set functions assign the \a newValue to this QAtomicInteger and + return \c true. If the values are \e not the same, the functions loads the + current value of this QAtomicInteger into \a currentValue and return \c false. + + \include qatomic.cpp memory-order-release +*/ + +/*! + \fn template <typename T> bool QAtomicInteger<T>::testAndSetOrdered(T expectedValue, T newValue, T ¤tValue) + \since 5.3 + + Atomic test-and-set. + + If the current value of this QAtomicInteger is the \a expectedValue, the + test-and-set functions assign the \a newValue to this QAtomicInteger and + return \c true. If the values are \e not the same, it loads the current + value of this QAtomicInteger into \a currentValue and return \c false. + + \include qatomic.cpp memory-order-ordered */ /*! diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h index a4c9c23fbe..b11e3f1bdf 100644 --- a/src/corelib/thread/qatomic.h +++ b/src/corelib/thread/qatomic.h @@ -70,6 +70,11 @@ public: bool testAndSetRelease(T expectedValue, T newValue); bool testAndSetOrdered(T expectedValue, T newValue); + bool testAndSetRelaxed(T expectedValue, T newValue, T ¤tValue); + bool testAndSetAcquire(T expectedValue, T newValue, T ¤tValue); + bool testAndSetRelease(T expectedValue, T newValue, T ¤tValue); + bool testAndSetOrdered(T expectedValue, T newValue, T ¤tValue); + static constexpr bool isFetchAndStoreNative(); static constexpr bool isFetchAndStoreWaitFree(); diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 8e96b943ef..80aa3e8d7a 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -590,14 +590,14 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func, { Q_ASSERT(f); - auto continuation = [func = std::forward<F>(func), promise = QPromise(fi), + auto continuation = [func = std::forward<F>(func), fi, context = QPointer<QObject>(context)]( const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); const auto parent = QFutureInterface<ParentResultType>(parentData).future(); QMetaObject::invokeMethod( context, - [func = std::forward<F>(func), promise = std::move(promise), parent]() mutable { + [func = std::forward<F>(func), promise = QPromise(fi), parent]() mutable { SyncContinuation<Function, ResultType, ParentResultType> continuationJob( std::forward<Function>(func), parent, std::move(promise)); continuationJob.execute(); @@ -689,13 +689,13 @@ void FailureHandler<Function, ResultType>::create(F &&function, QFuture<ResultTy Q_ASSERT(future); auto failureContinuation = - [function = std::forward<F>(function), promise = QPromise(fi), + [function = std::forward<F>(function), fi, context = QPointer<QObject>(context)](const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); const auto parent = QFutureInterface<ResultType>(parentData).future(); QMetaObject::invokeMethod(context, [function = std::forward<F>(function), - promise = std::move(promise), parent]() mutable { + promise = QPromise(fi), parent]() mutable { FailureHandler<Function, ResultType> failureHandler( std::forward<Function>(function), parent, std::move(promise)); failureHandler.run(); @@ -788,13 +788,13 @@ public: QObject *context) { Q_ASSERT(future); - auto canceledContinuation = [promise = QPromise(fi), handler = std::forward<F>(handler), + auto canceledContinuation = [fi, handler = std::forward<F>(handler), context = QPointer<QObject>(context)]( const QFutureInterfaceBase &parentData) mutable { Q_ASSERT(context); auto parentFuture = QFutureInterface<ResultType>(parentData).future(); QMetaObject::invokeMethod(context, - [promise = std::move(promise), parentFuture, + [promise = QPromise(fi), parentFuture, handler = std::forward<F>(handler)]() mutable { run(std::forward<F>(handler), parentFuture, std::move(promise)); }); diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index a82b7af873..ed46052fa7 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -105,6 +105,13 @@ void QFutureInterfaceBase::cancel(QFutureInterfaceBase::CancelMode mode) break; } + // Cancel the continuations chain + QFutureInterfaceBasePrivate *next = d->continuationData; + while (next) { + next->continuationState = QFutureInterfaceBasePrivate::Canceled; + next = next->continuationData; + } + d->waitCondition.wakeAll(); d->pausedWaitCondition.wakeAll(); @@ -719,7 +726,7 @@ void QFutureInterfaceBasePrivate::sendCallOut(const QFutureCallOutEvent &callOut if (outputConnections.isEmpty()) return; - for (int i = 0; i < outputConnections.count(); ++i) + for (int i = 0; i < outputConnections.size(); ++i) outputConnections.at(i)->postCallOutEvent(callOutEvent); } @@ -729,7 +736,7 @@ void QFutureInterfaceBasePrivate::sendCallOuts(const QFutureCallOutEvent &callOu if (outputConnections.isEmpty()) return; - for (int i = 0; i < outputConnections.count(); ++i) { + for (int i = 0; i < outputConnections.size(); ++i) { QFutureCallOutInterface *interface = outputConnections.at(i); interface->postCallOutEvent(callOutEvent1); interface->postCallOutEvent(callOutEvent2); @@ -816,45 +823,56 @@ void QFutureInterfaceBase::setContinuation(std::function<void(const QFutureInter { QMutexLocker lock(&d->continuationMutex); - if (continuationFutureData) - continuationFutureData->parentData = d; - // If the state is ready, run continuation immediately, // otherwise save it for later. if (isFinished()) { lock.unlock(); func(*this); - } else { + lock.relock(); + } + // Unless the continuation has been cleaned earlier, we have to + // store the move-only continuation, to guarantee that the associated + // future's data stays alive. + if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) { d->continuation = std::move(func); + d->continuationData = continuationFutureData; } } +void QFutureInterfaceBase::cleanContinuation() +{ + if (!d) + return; + + QMutexLocker lock(&d->continuationMutex); + d->continuation = nullptr; + d->continuationState = QFutureInterfaceBasePrivate::Cleaned; + d->continuationData = nullptr; +} + void QFutureInterfaceBase::runContinuation() const { QMutexLocker lock(&d->continuationMutex); if (d->continuation) { - auto fn = std::exchange(d->continuation, nullptr); + // Save the continuation in a local function, to avoid calling + // a null std::function below, in case cleanContinuation() is + // called from some other thread right after unlock() below. + auto fn = std::move(d->continuation); lock.unlock(); fn(*this); + + lock.relock(); + // Unless the continuation has been cleaned earlier, we have to + // store the move-only continuation, to guarantee that the associated + // future's data stays alive. + if (d->continuationState != QFutureInterfaceBasePrivate::Cleaned) + d->continuation = std::move(fn); } } bool QFutureInterfaceBase::isChainCanceled() const { - if (isCanceled()) - return true; - - auto parent = d->parentData; - while (parent) { - // If the future is in Canceled state because it had an exception, we want to - // continue checking the chain of parents for cancellation, otherwise if the exception - // is handled inside the chain, it won't be interrupted even though cancellation has - // been requested. - if ((parent->state.loadRelaxed() & Canceled) && !parent->hasException) - return true; - parent = parent->parentData; - } - return false; + return isCanceled() || d->continuationState == QFutureInterfaceBasePrivate::Canceled; } void QFutureInterfaceBase::setLaunchAsync(bool value) diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index 820faa9ec2..418f19866d 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -183,9 +183,7 @@ protected: void setContinuation(std::function<void(const QFutureInterfaceBase &)> func); void setContinuation(std::function<void(const QFutureInterfaceBase &)> func, QFutureInterfaceBasePrivate *continuationFutureData); -#if QT_CORE_REMOVED_SINCE(6, 4) void cleanContinuation(); -#endif void runContinuation() const; void setLaunchAsync(bool value); @@ -349,7 +347,7 @@ inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beg if (store.filterMode()) { this->reportResultsReady(resultCountBefore, store.count()); } else { - this->reportResultsReady(insertIndex, insertIndex + _results.count()); + this->reportResultsReady(insertIndex, insertIndex + _results.size()); } return true; } diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h index ec3517bab3..6258e61de7 100644 --- a/src/corelib/thread/qfutureinterface_p.h +++ b/src/corelib/thread/qfutureinterface_p.h @@ -141,7 +141,10 @@ public: QThreadPool *m_pool = nullptr; // Wrapper for continuation std::function<void(const QFutureInterfaceBase &)> continuation; - QFutureInterfaceBasePrivate *parentData = nullptr; + QFutureInterfaceBasePrivate *continuationData = nullptr; + + enum ContinuationState : quint8 { Default, Canceled, Cleaned }; + std::atomic<ContinuationState> continuationState { Default }; RefCount refCount = 1; QAtomicInt state; // reads and writes can happen unprotected, both must be atomic diff --git a/src/corelib/thread/qfuturesynchronizer.h b/src/corelib/thread/qfuturesynchronizer.h index 8b85e20038..2cf2ca1b76 100644 --- a/src/corelib/thread/qfuturesynchronizer.h +++ b/src/corelib/thread/qfuturesynchronizer.h @@ -38,12 +38,12 @@ public: void waitForFinished() { if (m_cancelOnWait) { - for (int i = 0; i < m_futures.count(); ++i) { + for (int i = 0; i < m_futures.size(); ++i) { m_futures[i].cancel(); } } - for (int i = 0; i < m_futures.count(); ++i) { + for (int i = 0; i < m_futures.size(); ++i) { m_futures[i].waitForFinished(); } } diff --git a/src/corelib/thread/qlocking_p.h b/src/corelib/thread/qlocking_p.h index 0c205fff66..9fa7e70da9 100644 --- a/src/corelib/thread/qlocking_p.h +++ b/src/corelib/thread/qlocking_p.h @@ -8,10 +8,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header -// file may change from version to version without notice, or even be -// removed. +// This file is not part of the Qt API. It exists for the convenience of +// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index fb15606ec0..7b4aac9532 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -865,12 +865,10 @@ void QMutexPrivate::derefWaiters(int value) noexcept QT_END_NAMESPACE -#if defined(Q_OS_LINUX) && defined(QT_ALWAYS_USE_FUTEX) +#if defined(QT_ALWAYS_USE_FUTEX) // nothing #elif defined(Q_OS_MAC) # include "qmutex_mac.cpp" -#elif defined(Q_OS_WIN) -# include "qmutex_win.cpp" #else # include "qmutex_unix.cpp" #endif diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 036fde729b..ce7b5275ac 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -9,10 +9,8 @@ #include <QtCore/qtsan_impl.h> #include <new> -#if __has_include(<chrono>) -# include <chrono> -# include <limits> -#endif +#include <chrono> +#include <limits> class tst_QMutex; @@ -31,7 +29,6 @@ class QMutex; class QRecursiveMutex; class QMutexPrivate; -#if __has_include(<chrono>) namespace QtPrivate { template<class Rep, class Period> @@ -57,7 +54,6 @@ namespace QtPrivate return ms < maxInt ? int(ms) : maxInt; } } -#endif class Q_CORE_EXPORT QBasicMutex { diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 9985a068c5..e5932d0a66 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -10,10 +10,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header -// file may change from version to version without notice, or even be -// removed. +// This file is not part of the Qt API. It exists for the convenience of +// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // @@ -90,8 +89,6 @@ public: bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; -#elif defined(Q_OS_WIN) - Qt::HANDLE event; #endif }; diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp deleted file mode 100644 index 8c7741c113..0000000000 --- a/src/corelib/thread/qmutex_win.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "qmutex.h" -#include <qatomic.h> -#include "qmutex_p.h" -#include <qt_windows.h> - -QT_BEGIN_NAMESPACE - -QMutexPrivate::QMutexPrivate() -{ - event = CreateEvent(0, FALSE, FALSE, 0); - - if (!event) - qWarning("QMutexPrivate::QMutexPrivate: Cannot create event"); -} - -QMutexPrivate::~QMutexPrivate() -{ CloseHandle(event); } - -bool QMutexPrivate::wait(int timeout) -{ - return (WaitForSingleObjectEx(event, timeout < 0 ? INFINITE : timeout, FALSE) == WAIT_OBJECT_0); -} - -void QMutexPrivate::wakeUp() noexcept -{ SetEvent(event); } - -QT_END_NAMESPACE diff --git a/src/corelib/thread/qpromise.h b/src/corelib/thread/qpromise.h index d0f6e6ae66..43c30582be 100644 --- a/src/corelib/thread/qpromise.h +++ b/src/corelib/thread/qpromise.h @@ -41,6 +41,7 @@ public: d.cancelAndFinish(); // cancel and finalize the state d.runContinuation(); } + d.cleanContinuation(); } // Core QPromise APIs diff --git a/src/corelib/thread/qpromise.qdoc b/src/corelib/thread/qpromise.qdoc index 09907c4e0e..a3af927e8d 100644 --- a/src/corelib/thread/qpromise.qdoc +++ b/src/corelib/thread/qpromise.qdoc @@ -38,6 +38,8 @@ \snippet snippet_qpromise.cpp multithread_init \codeline \snippet snippet_qpromise.cpp multithread_main + \codeline + \snippet snippet_qpromise.cpp multithread_cleanup \sa QFuture */ diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h index f633ed7801..af559b7a96 100644 --- a/src/corelib/thread/qresultstore.h +++ b/src/corelib/thread/qresultstore.h @@ -71,7 +71,7 @@ public: template <typename T> T *pointer() { - const T *p = qAsConst(*this).pointer<T>(); + const T *p = std::as_const(*this).pointer<T>(); return const_cast<T *>(p); } @@ -164,23 +164,23 @@ public: if (containsValidResultItem(index)) // reject if already present return -1; - return addResults(index, new QList<T>(*results), results->count(), results->count()); + return addResults(index, new QList<T>(*results), results->size(), results->size()); } template<typename T> int addResults(int index, const QList<T> *results, int totalCount) { // reject if results are empty, and nothing is filtered away - if ((m_filterMode == false || results->count() == totalCount) && results->empty()) + if ((m_filterMode == false || results->size() == totalCount) && results->empty()) return -1; if (containsValidResultItem(index)) // reject if already present return -1; - if (m_filterMode == true && results->count() != totalCount && 0 == results->count()) + if (m_filterMode == true && results->size() != totalCount && 0 == results->size()) return addResults(index, nullptr, 0, totalCount); - return addResults(index, new QList<T>(*results), results->count(), totalCount); + return addResults(index, new QList<T>(*results), results->size(), totalCount); } int addCanceledResult(int index) diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index 664085eb2b..803aa30bef 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -161,7 +161,7 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu if constexpr (futexHasWaiterCount) { Q_ASSERT(n > 1); ptr = futexHigh32(&u); - curValue >>= 32; + curValue = quint64(curValue) >> 32; } } @@ -213,7 +213,8 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp if constexpr (futexHasWaiterCount) { // We don't use the fetched value from above so futexWait() fails if // it changed after the testAndSetOrdered above. - if (((curValue >> 32) & 0x7fffffffU) == 0x7fffffffU) { + quint32 waiterCount = (quint64(curValue) >> 32) & 0x7fffffffU; + if (waiterCount == 0x7fffffffU) { qCritical() << "Waiter count overflow in QSemaphore"; return false; } diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index e294fe2fe0..e2f2707520 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -979,6 +979,16 @@ bool QThread::isRunning() const return d->running; } +void QThread::requestInterruption() +{ + +} + +bool QThread::isInterruptionRequested() const +{ + return false; +} + // No threads: so we can just use static variables Q_CONSTINIT static QThreadData *data = nullptr; diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 4cea76cf77..d856d8daa8 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -155,12 +155,12 @@ inline Qt::HANDLE QThread::currentThreadId() noexcept Qt::HANDLE tid; // typedef to void* static_assert(sizeof(tid) == sizeof(void*)); // See https://akkadia.org/drepper/tls.pdf for x86 ABI -#if defined(Q_PROCESSOR_X86_32) && defined(Q_OS_LINUX) && defined(__GLIBC__) // x86 32-bit always uses GS +#if defined(Q_PROCESSOR_X86_32) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD)) // x86 32-bit always uses GS __asm__("movl %%gs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : ); #elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_DARWIN64) // 64bit macOS uses GS, see https://github.com/apple/darwin-xnu/blob/master/libsyscall/os/tsd.h __asm__("movq %%gs:0, %0" : "=r" (tid) : : ); -#elif defined(Q_PROCESSOR_X86_64) && (defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD) +#elif defined(Q_PROCESSOR_X86_64) && ((defined(Q_OS_LINUX) && defined(__GLIBC__)) || defined(Q_OS_FREEBSD)) // x86_64 Linux, BSD uses FS __asm__("movq %%fs:%c1, %0" : "=r" (tid) : "i" (2 * sizeof(void*)) : ); #elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN) diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index d7e5135199..777aa362b2 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -137,8 +137,7 @@ static void set_thread_data(QThreadData *data) static void clear_thread_data() { - currentThreadData = nullptr; - pthread_setspecific(current_thread_data_key, nullptr); + set_thread_data(nullptr); } template <typename T> diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index b1078dab18..51783321b9 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -188,7 +188,7 @@ inline bool comparePriority(int priority, const QueuePage *p) void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority) { Q_ASSERT(runnable != nullptr); - for (QueuePage *page : qAsConst(queue)) { + for (QueuePage *page : std::as_const(queue)) { if (page->priority() == priority && !page->isFull()) { page->push(runnable); return; @@ -200,9 +200,9 @@ void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority) int QThreadPoolPrivate::activeThreadCount() const { - return (allThreads.count() - - expiredThreads.count() - - waitingThreads.count() + return (allThreads.size() + - expiredThreads.size() + - waitingThreads.size() + reservedThreads); } @@ -269,7 +269,7 @@ void QThreadPoolPrivate::reset() mutex.unlock(); - for (QThreadPoolThread *thread : qAsConst(allThreadsCopy)) { + for (QThreadPoolThread *thread : std::as_const(allThreadsCopy)) { if (thread->isRunning()) { thread->runnableReady.wakeAll(); thread->wait(); @@ -348,7 +348,7 @@ bool QThreadPool::tryTake(QRunnable *runnable) return false; QMutexLocker locker(&d->mutex); - for (QueuePage *page : qAsConst(d->queue)) { + for (QueuePage *page : std::as_const(d->queue)) { if (page->tryTake(runnable)) { if (page->isFinished()) { d->queue.removeOne(page); @@ -475,6 +475,21 @@ QThreadPool *QThreadPool::globalInstance() } /*! + Returns the QThreadPool instance for Qt Gui. + \internal +*/ +QThreadPool *QThreadPoolPrivate::qtGuiInstance() +{ + Q_CONSTINIT static QPointer<QThreadPool> guiInstance; + Q_CONSTINIT static QBasicMutex theMutex; + + const QMutexLocker locker(&theMutex); + if (guiInstance.isNull() && !QCoreApplication::closingDown()) + guiInstance = new QThreadPool(); + return guiInstance; +} + +/*! Reserves a thread and uses it to run \a runnable, unless this thread will make the current thread count exceed maxThreadCount(). In that case, \a runnable is added to a run queue instead. The \a priority argument can @@ -590,12 +605,14 @@ bool QThreadPool::tryStart(std::function<void()> functionToRun) int QThreadPool::expiryTimeout() const { Q_D(const QThreadPool); + QMutexLocker locker(&d->mutex); return d->expiryTimeout; } void QThreadPool::setExpiryTimeout(int expiryTimeout) { Q_D(QThreadPool); + QMutexLocker locker(&d->mutex); if (d->expiryTimeout == expiryTimeout) return; d->expiryTimeout = expiryTimeout; @@ -616,6 +633,7 @@ void QThreadPool::setExpiryTimeout(int expiryTimeout) int QThreadPool::maxThreadCount() const { Q_D(const QThreadPool); + QMutexLocker locker(&d->mutex); return d->requestedMaxThreadCount; } @@ -685,12 +703,14 @@ void QThreadPool::reserveThread() void QThreadPool::setStackSize(uint stackSize) { Q_D(QThreadPool); + QMutexLocker locker(&d->mutex); d->stackSize = stackSize; } uint QThreadPool::stackSize() const { Q_D(const QThreadPool); + QMutexLocker locker(&d->mutex); return d->stackSize; } @@ -711,12 +731,14 @@ uint QThreadPool::stackSize() const void QThreadPool::setThreadPriority(QThread::Priority priority) { Q_D(QThreadPool); + QMutexLocker locker(&d->mutex); d->threadPriority = priority; } QThread::Priority QThreadPool::threadPriority() const { Q_D(const QThreadPool); + QMutexLocker locker(&d->mutex); return d->threadPriority; } diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h index f967880bde..67c703fabd 100644 --- a/src/corelib/thread/qthreadpool_p.h +++ b/src/corelib/thread/qthreadpool_p.h @@ -134,6 +134,8 @@ public: void stealAndRunRunnable(QRunnable *runnable); void deletePageIfFinished(QueuePage *page); + static QThreadPool *qtGuiInstance(); + mutable QMutex mutex; QSet<QThreadPoolThread *> allThreads; QQueue<QThreadPoolThread *> waitingThreads; diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 55a603e0e6..c2029fe1b3 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -51,15 +51,15 @@ QThreadStorageData::QThreadStorageData(void (*func)(void *)) no where to store it, and no way to actually call it. */ QThreadData *data = QThreadData::current(); - id = data->tls.count(); + id = data->tls.size(); DEBUG_MSG("QThreadStorageData: Allocated id %d, destructor %p cannot be stored", id, func); return; } - for (id = 0; id < destr->count(); id++) { + for (id = 0; id < destr->size(); id++) { if (destr->at(id) == nullptr) break; } - if (id == destr->count()) { + if (id == destr->size()) { destr->append(func); } else { (*destr)[id] = func; diff --git a/src/corelib/thread/qwaitcondition.qdoc b/src/corelib/thread/qwaitcondition.qdoc index bd3a675192..435771e1d8 100644 --- a/src/corelib/thread/qwaitcondition.qdoc +++ b/src/corelib/thread/qwaitcondition.qdoc @@ -98,10 +98,16 @@ /*! \fn bool QWaitCondition::wait(QMutex *lockedMutex, unsigned long time) \overload + + Releases the \a lockedMutex and waits on the wait condition for \a time + milliseconds. */ /*! \fn bool QWaitCondition::wait(QReadWriteLock *lockedReadWriteLock, unsigned long time) \overload + + Releases the \a lockedReadWriteLock and waits on the wait condition for \a + time milliseconds. */ /*! diff --git a/src/corelib/thread/qwaitcondition_p.h b/src/corelib/thread/qwaitcondition_p.h index e82f832817..0893e9dd35 100644 --- a/src/corelib/thread/qwaitcondition_p.h +++ b/src/corelib/thread/qwaitcondition_p.h @@ -7,10 +7,9 @@ // W A R N I N G // ------------- // -// This file is not part of the Qt API. It exists for the convenience -// of qmutex.cpp, qmutex_unix.cpp, and qmutex_win.cpp. This header -// file may change from version to version without notice, or even be -// removed. +// This file is not part of the Qt API. It exists for the convenience of +// qmutex.cpp and qmutex_unix.cpp. This header file may change from version to +// version without notice, or even be removed. // // We mean it. // diff --git a/src/corelib/thread/qwaitcondition_win.cpp b/src/corelib/thread/qwaitcondition_win.cpp index 0deef043cd..2152ae551f 100644 --- a/src/corelib/thread/qwaitcondition_win.cpp +++ b/src/corelib/thread/qwaitcondition_win.cpp @@ -178,7 +178,7 @@ void QWaitCondition::wakeOne() { // wake up the first waiting thread in the queue QMutexLocker locker(&d->mtx); - for (QWaitConditionEvent *current : qAsConst(d->queue)) { + for (QWaitConditionEvent *current : std::as_const(d->queue)) { if (current->wokenUp) continue; SetEvent(current->event); @@ -191,7 +191,7 @@ void QWaitCondition::wakeAll() { // wake up the all threads in the queue QMutexLocker locker(&d->mtx); - for (QWaitConditionEvent *current : qAsConst(d->queue)) { + for (QWaitConditionEvent *current : std::as_const(d->queue)) { SetEvent(current->event); current->wokenUp = true; } |