summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qthreadpool.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/thread/qthreadpool.cpp')
-rw-r--r--src/corelib/thread/qthreadpool.cpp170
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