diff options
Diffstat (limited to 'src/corelib/thread/qthreadpool.cpp')
-rw-r--r-- | src/corelib/thread/qthreadpool.cpp | 170 |
1 files changed, 78 insertions, 92 deletions
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index 713c436371..ae584656fe 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -1,51 +1,20 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "qthreadpool.h" #include "qthreadpool_p.h" #include "qdeadlinetimer.h" #include "qcoreapplication.h" +#include <QtCore/qpointer.h> + #include <algorithm> +#include <memory> QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + /* QThread wrapper, provides synchronization against a ThreadPool */ @@ -120,7 +89,7 @@ void QThreadPoolThread::run() if (manager->queue.isEmpty()) break; - QueuePage *page = manager->queue.first(); + QueuePage *page = manager->queue.constFirst(); r = page->pop(); if (page->isFinished()) { @@ -221,7 +190,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; @@ -233,9 +202,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); } @@ -243,7 +212,7 @@ void QThreadPoolPrivate::tryToStartMoreThreads() { // try to push tasks on the queue to any available threads while (!queue.isEmpty()) { - QueuePage *page = queue.first(); + QueuePage *page = queue.constFirst(); if (!tryStart(page->first())) break; @@ -273,21 +242,17 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const */ void QThreadPoolPrivate::startThread(QRunnable *runnable) { - Q_Q(QThreadPool); Q_ASSERT(runnable != nullptr); - QScopedPointer<QThreadPoolThread> thread(new QThreadPoolThread(this)); - QString objectName; - if (QString myName = q->objectName(); !myName.isEmpty()) - objectName = myName; - else - objectName = QLatin1String("Thread (pooled)"); + auto thread = std::make_unique<QThreadPoolThread>(this); + if (objectName.isEmpty()) + objectName = u"Thread (pooled)"_s; thread->setObjectName(objectName); - Q_ASSERT(!allThreads.contains(thread.data())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here) - allThreads.insert(thread.data()); + Q_ASSERT(!allThreads.contains(thread.get())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here) + allThreads.insert(thread.get()); ++activeThreads; thread->runnable = runnable; - thread.take()->start(threadPriority); + thread.release()->start(threadPriority); } /*! @@ -306,7 +271,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(); @@ -385,7 +350,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); @@ -476,7 +441,14 @@ void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable) */ QThreadPool::QThreadPool(QObject *parent) : QObject(*new QThreadPoolPrivate, parent) -{ } +{ + Q_D(QThreadPool); + connect(this, &QObject::objectNameChanged, this, [d](const QString &newName) { + // We keep a copy of the name under our own lock, so we can access it thread-safely. + QMutexLocker locker(&d->mutex); + d->objectName = newName; + }); +} /*! Destroys the QThreadPool. @@ -495,8 +467,8 @@ QThreadPool::~QThreadPool() */ QThreadPool *QThreadPool::globalInstance() { - static QPointer<QThreadPool> theInstance; - static QBasicMutex theMutex; + Q_CONSTINIT static QPointer<QThreadPool> theInstance; + Q_CONSTINIT static QBasicMutex theMutex; const QMutexLocker locker(&theMutex); if (theInstance.isNull() && !QCoreApplication::closingDown()) @@ -505,6 +477,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 @@ -532,20 +519,21 @@ void QThreadPool::start(QRunnable *runnable, int priority) } /*! + \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::start(Callable &&callableToRun, int priority) \overload \since 5.15 - Reserves a thread and uses it to run \a functionToRun, unless this thread will + Reserves a thread and uses it to run \a callableToRun, unless this thread will make the current thread count exceed maxThreadCount(). In that case, - \a functionToRun is added to a run queue instead. The \a priority argument can + \a callableToRun is added to a run queue instead. The \a priority argument can be used to control the run queue's order of execution. + + \note This function participates in overload resolution only if \c Callable + is a function or function object which can be called with zero arguments. + + \note In Qt version prior to 6.6, this function took std::function<void()>, + and therefore couldn't handle move-only callables. */ -void QThreadPool::start(std::function<void()> functionToRun, int priority) -{ - if (!functionToRun) - return; - start(QRunnable::create(std::move(functionToRun)), priority); -} /*! Attempts to reserve a thread to run \a runnable. @@ -577,30 +565,21 @@ bool QThreadPool::tryStart(QRunnable *runnable) } /*! + \fn template<typename Callable, QRunnable::if_callable<Callable>> bool QThreadPool::tryStart(Callable &&callableToRun) \overload \since 5.15 - Attempts to reserve a thread to run \a functionToRun. + Attempts to reserve a thread to run \a callableToRun. If no threads are available at the time of calling, then this function - does nothing and returns \c false. Otherwise, \a functionToRun is run immediately + does nothing and returns \c false. Otherwise, \a callableToRun is run immediately using one available thread and this function returns \c true. -*/ -bool QThreadPool::tryStart(std::function<void()> functionToRun) -{ - if (!functionToRun) - return false; - Q_D(QThreadPool); - QMutexLocker locker(&d->mutex); - if (!d->allThreads.isEmpty() && d->areAllThreadsActive()) - return false; + \note This function participates in overload resolution only if \c Callable + is a function or function object which can be called with zero arguments. - QRunnable *runnable = QRunnable::create(std::move(functionToRun)); - if (d->tryStart(runnable)) - return true; - delete runnable; - return false; -} + \note In Qt version prior to 6.6, this function took std::function<void()>, + and therefore couldn't handle move-only callables. +*/ /*! \property QThreadPool::expiryTimeout \brief the thread expiry timeout value in milliseconds. @@ -620,12 +599,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; @@ -646,6 +627,7 @@ void QThreadPool::setExpiryTimeout(int expiryTimeout) int QThreadPool::maxThreadCount() const { Q_D(const QThreadPool); + QMutexLocker locker(&d->mutex); return d->requestedMaxThreadCount; } @@ -715,12 +697,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; } @@ -741,12 +725,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; } @@ -807,19 +793,19 @@ void QThreadPool::startOnReservedThread(QRunnable *runnable) } /*! + \fn template<typename Callable, QRunnable::if_callable<Callable>> void QThreadPool::startOnReservedThread(Callable &&callableToRun) \overload \since 6.3 Releases a thread previously reserved with reserveThread() and uses it - to run \a functionToRun. -*/ -void QThreadPool::startOnReservedThread(std::function<void()> functionToRun) -{ - if (!functionToRun) - return releaseThread(); + to run \a callableToRun. - startOnReservedThread(QRunnable::create(std::move(functionToRun))); -} + \note This function participates in overload resolution only if \c Callable + is a function or function object which can be called with zero arguments. + + \note In Qt version prior to 6.6, this function took std::function<void()>, + and therefore couldn't handle move-only callables. +*/ /*! Waits up to \a msecs milliseconds for all threads to exit and removes all |