drop QThreadPool and QRunnable

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2024-04-18 02:58:30 +03:00
parent cd3d86140e
commit b2504b1717
12 changed files with 0 additions and 1836 deletions

View file

@ -108,7 +108,6 @@ include/katie/QtCore/QRect
include/katie/QtCore/QRectF
include/katie/QtCore/QRegExp
include/katie/QtCore/QReturnArgument
include/katie/QtCore/QRunnable
include/katie/QtCore/QScopedPointer
include/katie/QtCore/QScopedPointerPodDeleter
include/katie/QtCore/QScopedValueRollback
@ -136,7 +135,6 @@ include/katie/QtCore/QTextConverter
include/katie/QtCore/QTextStream
include/katie/QtCore/QTextStreamFunction
include/katie/QtCore/QThread
include/katie/QtCore/QThreadPool
include/katie/QtCore/QTime
include/katie/QtCore/QTimeLine
include/katie/QtCore/QTimer
@ -226,7 +224,6 @@ include/katie/QtCore/qprocess.h
include/katie/QtCore/qqueue.h
include/katie/QtCore/qrect.h
include/katie/QtCore/qregexp.h
include/katie/QtCore/qrunnable.h
include/katie/QtCore/qscopedpointer.h
include/katie/QtCore/qscopedvaluerollback.h
include/katie/QtCore/qsemaphore.h
@ -246,7 +243,6 @@ include/katie/QtCore/qtemporaryfile.h
include/katie/QtCore/qtextcodec.h
include/katie/QtCore/qtextstream.h
include/katie/QtCore/qthread.h
include/katie/QtCore/qthreadpool.h
include/katie/QtCore/qtimeline.h
include/katie/QtCore/qtimer.h
include/katie/QtCore/qtranslator.h

View file

@ -350,7 +350,6 @@ classlist = [
"QReturnArgument",
"QRgb",
"QRubberBand",
"QRunnable",
"QScopedPointer",
"QScopedPointerPodDeleter",
"QScopedValueRollback",
@ -493,7 +492,6 @@ classlist = [
"QTextTableCellFormat",
"QTextTableFormat",
"QThread",
"QThreadPool",
"QTileRules",
"QTime",
"QTimeEdit",

View file

@ -28,7 +28,6 @@ set(CORE_PUBLIC_HEADERS
QDebug
QFileInfo
QDateTime
QThreadPool
QSettings
QStandardPaths
QPlugin
@ -74,7 +73,6 @@ set(CORE_PUBLIC_HEADERS
QFile
QCache
QAbstractEventDispatcher
QRunnable
QTimeLine
QTextCodec
QStringList
@ -170,9 +168,6 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/thread/qmutexpool_p.h
${CMAKE_CURRENT_SOURCE_DIR}/thread/qorderedmutexlocker_p.h
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthread_p.h
${CMAKE_CURRENT_SOURCE_DIR}/thread/qrunnable.h
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthreadpool.h
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthreadpool_p.h
${CMAKE_CURRENT_SOURCE_DIR}/tools/qalgorithms.h
${CMAKE_CURRENT_SOURCE_DIR}/tools/qbitarray.h
${CMAKE_CURRENT_SOURCE_DIR}/tools/qbytearray.h
@ -270,10 +265,8 @@ set(CORE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/plugin/qlibrary_unix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qatomic.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qmutex.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qrunnable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qsemaphore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthread.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthreadpool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qthread_unix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/thread/qwaitcondition_unix.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tools/qbitarray.cpp

View file

@ -33,7 +33,6 @@
#include "qhash.h"
#include "qtextcodec.h"
#include "qthread.h"
#include "qthreadpool.h"
#include "qstandardpaths.h"
#include "qelapsedtimer.h"
#include "qscopedpointer.h"
@ -361,13 +360,6 @@ QCoreApplication::~QCoreApplication()
QCoreApplicationPrivate::is_app_closing = true;
QCoreApplicationPrivate::is_app_running = false;
#if !defined(QT_NO_THREAD)
// Synchronize and stop the global thread pool threads.
QThreadPool *globalThreadPool = QThreadPool::globalInstance();
if (globalThreadPool)
globalThreadPool->waitForDone();
#endif
#ifndef QT_NO_LIBRARY
coreappdata()->app_librarypaths.clear();
coreappdata()->app_pluginpaths.clear();

View file

@ -1,87 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the QtCore module of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\class QRunnable
\since 4.4
\brief The QRunnable class is the base class for all runnable objects.
\ingroup thread
The QRunnable class is an interface for representing a task or
piece of code that needs to be executed, represented by your
reimplementation of the run() function.
You can use QThreadPool to execute your code in a separate
thread. QThreadPool deletes the QRunnable automatically if
autoDelete() returns true (the default). Use setAutoDelete() to
change the auto-deletion flag.
QThreadPool supports executing the same QRunnable more than once
by calling QThreadPool::tryStart(this) from within the run() function.
If autoDelete is enabled the QRunnable will be deleted when
the last thread exits the run function. Calling QThreadPool::start()
multiple times with the same QRunnable when autoDelete is enabled
creates a race condition and is not recommended.
\sa QThreadPool
*/
/*! \fn QRunnable::run()
Implement this pure virtual function in your subclass.
*/
/*! \fn QRunnable::QRunnable()
Constructs a QRunnable. Auto-deletion is enabled by default.
\sa autoDelete(), setAutoDelete()
*/
/*! \fn QRunnable::~QRunnable()
QRunnable virtual destructor.
*/
/*! \fn bool QRunnable::autoDelete() const
Returns true is auto-deletion is enabled; false otherwise.
If auto-deletion is enabled, QThreadPool will automatically delete
this runnable after calling run(); otherwise, ownership remains
with the application programmer.
\sa setAutoDelete(), QThreadPool
*/
/*! \fn bool QRunnable::setAutoDelete(bool autoDelete)
Enables auto-deletion if \a autoDelete is true; otherwise
auto-deletion is disabled.
If auto-deletion is enabled, QThreadPool will automatically delete
this runnable after calling run(); otherwise, ownership remains
with the application programmer.
Note that this flag must be set before calling
QThreadPool::start(). Calling this function after
QThreadPool::start() results in undefined behavior.
\sa autoDelete(), QThreadPool
*/

View file

@ -1,50 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the QtCore module of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QRUNNABLE_H
#define QRUNNABLE_H
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
class QRunnable
{
public:
virtual void run() = 0;
QRunnable() : ref(0) { }
virtual ~QRunnable() { }
bool autoDelete() const { return ref != -1; }
void setAutoDelete(bool _autoDelete) { ref = _autoDelete ? 0 : -1; }
private:
int ref;
friend class QThreadPoolPrivate;
friend class QThreadPoolThread;
};
QT_END_NAMESPACE
#endif

View file

@ -1,573 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the QtCore module of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qthreadpool.h"
#include "qthreadpool_p.h"
#include "qelapsedtimer.h"
#include "qscopedpointer.h"
#ifndef QT_NO_THREAD
QT_BEGIN_NAMESPACE
inline bool operator<(int priority, const QPair<QRunnable *, int> &p)
{
return p.second < priority;
}
inline bool operator<(const QPair<QRunnable *, int> &p, int priority)
{
return priority < p.second;
}
Q_GLOBAL_STATIC(QThreadPool, theInstance)
/*
QThread wrapper, provides synchronization against a ThreadPool
*/
class QThreadPoolThread : public QThread
{
public:
QThreadPoolThread(QThreadPoolPrivate *manager);
void run() final;
void registerThreadInactive();
QWaitCondition runnableReady;
QThreadPoolPrivate *manager;
QRunnable *runnable;
};
/*
QThreadPool private class.
*/
QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
: manager(manager),
runnable(nullptr)
{
}
void QThreadPoolThread::run()
{
QMutexLocker locker(&manager->mutex);
for(;;) {
QRunnable *r = runnable;
runnable = nullptr;
do {
if (r) {
const bool autoDelete = r->autoDelete();
// run the task
locker.unlock();
QT_TRY {
r->run();
} QT_CATCH (...) {
qWarning("Qt Concurrent has caught an exception thrown from a worker thread.\n"
"This is not supported, exceptions thrown in worker threads must be\n"
"caught before control returns to Qt Concurrent.");
registerThreadInactive();
QT_RETHROW;
}
locker.relock();
if (autoDelete && !--r->ref) {
delete r;
}
}
// if too many threads are active, expire this thread
if (manager->tooManyThreadsActive()) {
break;
}
r = !manager->queue.isEmpty() ? manager->queue.takeFirst().first : nullptr;
} while (r != nullptr);
if (manager->isExiting) {
registerThreadInactive();
break;
}
// if too many threads are active, expire this thread
bool expired = manager->tooManyThreadsActive();
if (!expired) {
manager->waitingThreads.enqueue(this);
registerThreadInactive();
// wait for work, exiting after the expiry timeout is reached
runnableReady.wait(locker.mutex(), manager->expiryTimeout);
++manager->activeThreads;
if (manager->waitingThreads.removeOne(this)) {
expired = true;
}
}
if (expired) {
manager->expiredThreads.enqueue(this);
registerThreadInactive();
break;
}
}
}
void QThreadPoolThread::registerThreadInactive()
{
if (--manager->activeThreads == 0) {
manager->noActiveThreads.wakeAll();
}
}
/* \internal
*/
QThreadPoolPrivate:: QThreadPoolPrivate()
: isExiting(false),
expiryTimeout(30000),
maxThreadCount(qAbs(QThread::idealThreadCount())),
reservedThreads(0),
activeThreads(0)
{
}
bool QThreadPoolPrivate::tryStart(QRunnable *task)
{
if (allThreads.isEmpty()) {
// always create at least one thread
startThread(task);
return true;
}
// can't do anything if we're over the limit
if (activeThreadCount() >= maxThreadCount) {
return false;
}
if (waitingThreads.count() > 0) {
// recycle an available thread
enqueueTask(task);
waitingThreads.takeFirst()->runnableReady.wakeOne();
return true;
}
if (!expiredThreads.isEmpty()) {
// restart an expired thread
QThreadPoolThread *thread = expiredThreads.dequeue();
Q_ASSERT(thread->runnable == nullptr);
++activeThreads;
if (task->autoDelete())
++task->ref;
thread->runnable = task;
thread->start();
return true;
}
// start a new thread
startThread(task);
return true;
}
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
{
if (runnable->autoDelete()) {
++runnable->ref;
}
// put it on the queue
QList<QPair<QRunnable *, int> >::iterator at = qUpperBound(queue.begin(), queue.end(), priority);
queue.insert(at, qMakePair(runnable, priority));
}
int QThreadPoolPrivate::activeThreadCount() const
{
return (allThreads.count() - expiredThreads.count() - waitingThreads.count() + reservedThreads);
}
void QThreadPoolPrivate::tryToStartMoreThreads()
{
// try to push tasks on the queue to any available threads
while (!queue.isEmpty() && tryStart(queue.first().first)) {
queue.removeFirst();
}
}
bool QThreadPoolPrivate::tooManyThreadsActive() const
{
const int activeThreadCount = this->activeThreadCount();
return activeThreadCount > maxThreadCount && (activeThreadCount - reservedThreads) > 1;
}
/*!
\internal
*/
void QThreadPoolPrivate::startThread(QRunnable *runnable)
{
QScopedPointer <QThreadPoolThread> thread(new QThreadPoolThread(this));
thread->setObjectName(QLatin1String("Thread (pooled)"));
allThreads.insert(thread.data());
++activeThreads;
if (runnable->autoDelete()) {
++runnable->ref;
}
thread->runnable = runnable;
thread.take()->start();
}
/*!
\internal
Makes all threads exit, waits for each thread to exit and deletes it.
*/
void QThreadPoolPrivate::reset()
{
QMutexLocker locker(&mutex);
isExiting = true;
do {
// make a copy of the set so that we can iterate without the lock
QSet<QThreadPoolThread *> allThreadsCopy = allThreads;
allThreads.clear();
locker.unlock();
foreach (QThreadPoolThread *thread, allThreadsCopy) {
thread->runnableReady.wakeAll();
thread->wait();
delete thread;
}
locker.relock();
// repeat until all newly arrived threads have also completed
} while (!allThreads.isEmpty());
waitingThreads.clear();
expiredThreads.clear();
isExiting = false;
}
bool QThreadPoolPrivate::waitForDone(int msecs)
{
QMutexLocker locker(&mutex);
if (msecs < 0) {
if (!queue.isEmpty() || activeThreads != 0) {
noActiveThreads.wait(locker.mutex());
}
} else {
if (!queue.isEmpty() || activeThreads != 0) {
noActiveThreads.wait(locker.mutex(), msecs);
}
}
return (queue.isEmpty() && activeThreads == 0);
}
/*!
\class QThreadPool
\brief The QThreadPool class manages a collection of QThreads.
\since 4.4
\threadsafe
\ingroup thread
QThreadPool manages and recyles individual QThread objects to help reduce
thread creation costs in programs that use threads. Each Qt application
has one global QThreadPool object, which can be accessed by calling
globalInstance().
To use one of the QThreadPool threads, subclass QRunnable and implement
the run() virtual function. Then create an object of that class and pass
it to QThreadPool::start().
\snippet doc/src/snippets/code/src_corelib_concurrent_qthreadpool.cpp 0
QThreadPool deletes the QRunnable automatically by default. Use
QRunnable::setAutoDelete() to change the auto-deletion flag.
QThreadPool supports executing the same QRunnable more than once
by calling tryStart(this) from within QRunnable::run().
If autoDelete is enabled the QRunnable will be deleted when
the last thread exits the run function. Calling start()
multiple times with the same QRunnable when autoDelete is enabled
creates a race condition and is not recommended.
Threads that are unused for a certain amount of time will expire. The
default expiry timeout is 30000 milliseconds (30 seconds). This can be
changed using setExpiryTimeout(). Setting a negative expiry timeout
disables the expiry mechanism.
Call maxThreadCount() to query the maximum number of threads to be used.
If needed, you can change the limit with setMaxThreadCount(). The default
maxThreadCount() is QThread::idealThreadCount(). The activeThreadCount()
function returns the number of threads currently doing work.
The reserveThread() function reserves a thread for external
use. Use releaseThread() when your are done with the thread, so
that it may be reused. Essentially, these functions temporarily
increase or reduce the active thread count and are useful when
implementing time-consuming operations that are not visible to the
QThreadPool.
Note that QThreadPool is a low-level class for managing threads, see
QtConcurrent::run() or the other
\l {Concurrent Programming}{Qt Concurrent} APIs for higher
level alternatives.
\sa QRunnable
*/
/*!
Constructs a thread pool with the given \a parent.
*/
QThreadPool::QThreadPool(QObject *parent)
: QObject(*new QThreadPoolPrivate, parent)
{
}
/*!
Destroys the QThreadPool.
This function will block until all runnables have been completed.
*/
QThreadPool::~QThreadPool()
{
waitForDone();
}
/*!
Returns the global QThreadPool instance.
*/
QThreadPool *QThreadPool::globalInstance()
{
return theInstance();
}
/*!
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
be used to control the run queue's order of execution.
Note that the thread pool takes ownership of the \a runnable if
\l{QRunnable::autoDelete()}{runnable->autoDelete()} returns true,
and the \a runnable will be deleted automatically by the thread
pool after the \l{QRunnable::run()}{runnable->run()} returns. If
\l{QRunnable::autoDelete()}{runnable->autoDelete()} returns false,
ownership of \a runnable remains with the caller. Note that
changing the auto-deletion on \a runnable after calling this
functions results in undefined behavior.
*/
void QThreadPool::start(QRunnable *runnable, int priority)
{
if (Q_UNLIKELY(!runnable)) {
return;
}
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (!d->tryStart(runnable)) {
d->enqueueTask(runnable, priority);
if (!d->waitingThreads.isEmpty()) {
d->waitingThreads.takeFirst()->runnableReady.wakeOne();
}
}
}
/*!
Attempts to reserve a thread to run \a runnable.
If no threads are available at the time of calling, then this function
does nothing and returns false. Otherwise, \a runnable is run immediately
using one available thread and this function returns true.
Note that the thread pool takes ownership of the \a runnable if
\l{QRunnable::autoDelete()}{runnable->autoDelete()} returns true,
and the \a runnable will be deleted automatically by the thread
pool after the \l{QRunnable::run()}{runnable->run()} returns. If
\l{QRunnable::autoDelete()}{runnable->autoDelete()} returns false,
ownership of \a runnable remains with the caller. Note that
changing the auto-deletion on \a runnable after calling this
function results in undefined behavior.
*/
bool QThreadPool::tryStart(QRunnable *runnable)
{
if (Q_UNLIKELY(!runnable)) {
return false;
}
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (d->allThreads.isEmpty() == false && d->activeThreadCount() >= d->maxThreadCount) {
return false;
}
return d->tryStart(runnable);
}
/*!
\property QThreadPool::expiryTimeout
Threads that are unused for \a expiryTimeout milliseconds are considered
to have expired and will exit. Such threads will be restarted as needed.
The default \a expiryTimeout is 30000 milliseconds (30 seconds). If
\a expiryTimeout is negative, newly created threads will not expire, e.g.,
they will not exit until the thread pool is destroyed.
Note that setting \a expiryTimeout has no effect on already running
threads. Only newly created threads will use the new \a expiryTimeout.
We recommend setting the \a expiryTimeout immediately after creating the
thread pool, but before calling start().
*/
int QThreadPool::expiryTimeout() const
{
Q_D(const QThreadPool);
return d->expiryTimeout;
}
void QThreadPool::setExpiryTimeout(int expiryTimeout)
{
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (d->expiryTimeout == expiryTimeout) {
return;
}
d->expiryTimeout = expiryTimeout;
}
/*!
\property QThreadPool::maxThreadCount
This property represents the maximum number of threads used by the thread
pool.
\note The thread pool will always use at least 1 thread, even if
\a maxThreadCount limit is zero or negative.
The default \a maxThreadCount is QThread::idealThreadCount().
*/
int QThreadPool::maxThreadCount() const
{
Q_D(const QThreadPool);
return d->maxThreadCount;
}
void QThreadPool::setMaxThreadCount(int maxThreadCount)
{
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (maxThreadCount == d->maxThreadCount) {
return;
}
d->maxThreadCount = maxThreadCount;
d->tryToStartMoreThreads();
}
/*!
\property QThreadPool::activeThreadCount
This property represents the number of active threads in the thread pool.
\note It is possible for this function to return a value that is greater
than maxThreadCount(). See reserveThread() for more details.
\sa reserveThread(), releaseThread()
*/
int QThreadPool::activeThreadCount() const
{
Q_D(const QThreadPool);
return d->activeThreadCount();
}
/*!
Reserves one thread, disregarding activeThreadCount() and maxThreadCount().
Once you are done with the thread, call releaseThread() to allow it to be
reused.
\note This function will always increase the number of active threads.
This means that by using this function, it is possible for
activeThreadCount() to return a value greater than maxThreadCount() .
\sa releaseThread()
*/
void QThreadPool::reserveThread()
{
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
++d->reservedThreads;
}
/*!
Releases a thread previously reserved by a call to reserveThread().
\note Calling this function without previously reserving a thread
temporarily increases maxThreadCount(). This is useful when a
thread goes to sleep waiting for more work, allowing other threads
to continue. Be sure to call reserveThread() when done waiting, so
that the thread pool can correctly maintain the
activeThreadCount().
\sa reserveThread()
*/
void QThreadPool::releaseThread()
{
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
--d->reservedThreads;
d->tryToStartMoreThreads();
}
/*!
Waits for each thread to exit and removes all threads from the thread pool.
*/
void QThreadPool::waitForDone()
{
Q_D(QThreadPool);
d->waitForDone();
d->reset();
}
/*!
\overload waitForDone()
\since 4.8
Waits up to \a msecs milliseconds for all threads to exit and removes all
threads from the thread pool. Returns true if all threads were removed;
otherwise it returns false.
*/
bool QThreadPool::waitForDone(int msecs)
{
Q_D(QThreadPool);
const bool rc = d->waitForDone(msecs);
if (rc) {
d->reset();
}
return rc;
}
#include "moc_qthreadpool.h"
QT_END_NAMESPACE
#endif

View file

@ -1,69 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the QtCore module of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTHREADPOOL_H
#define QTHREADPOOL_H
#include <QtCore/qthread.h>
#include <QtCore/qrunnable.h>
#ifndef QT_NO_THREAD
QT_BEGIN_NAMESPACE
class QThreadPoolPrivate;
class Q_CORE_EXPORT QThreadPool : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QThreadPool)
Q_PROPERTY(int expiryTimeout READ expiryTimeout WRITE setExpiryTimeout)
Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount)
Q_PROPERTY(int activeThreadCount READ activeThreadCount)
public:
QThreadPool(QObject *parent = nullptr);
~QThreadPool();
static QThreadPool *globalInstance();
void start(QRunnable *runnable, int priority = 0);
bool tryStart(QRunnable *runnable);
int expiryTimeout() const;
void setExpiryTimeout(int expiryTimeout);
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);
int activeThreadCount() const;
void reserveThread();
void releaseThread();
void waitForDone();
bool waitForDone(int msecs);
};
QT_END_NAMESPACE
#endif // QT_NO_THREAD
#endif

View file

@ -1,84 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the QtCore module of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTHREADPOOL_P_H
#define QTHREADPOOL_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Katie API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
//
#include "QtCore/qmutex.h"
#include "QtCore/qwaitcondition.h"
#include "QtCore/qset.h"
#include "QtCore/qqueue.h"
#include "qobject_p.h"
#ifndef QT_NO_THREAD
QT_BEGIN_NAMESPACE
class QThreadPoolThread;
class QThreadPoolPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QThreadPool)
friend class QThreadPoolThread;
public:
QThreadPoolPrivate();
bool tryStart(QRunnable *task);
void enqueueTask(QRunnable *task, int priority = 0);
int activeThreadCount() const;
void tryToStartMoreThreads();
bool tooManyThreadsActive() const;
void startThread(QRunnable *runnable);
void reset();
bool waitForDone(int msecs = -1);
QMutex mutex;
QSet<QThreadPoolThread *> allThreads;
QQueue<QThreadPoolThread *> waitingThreads;
QQueue<QThreadPoolThread *> expiredThreads;
QList<QPair<QRunnable *, int> > queue;
QWaitCondition noActiveThreads;
bool isExiting;
int expiryTimeout;
int maxThreadCount;
int reservedThreads;
int activeThreads;
};
QT_END_NAMESPACE
#endif // QT_NO_THREAD
#endif

View file

@ -477,7 +477,6 @@ static const struct ClassTblData {
{ QLatin1String("QTextTableCellFormat"), QLatin1String("QtGui/qtextformat.h") },
{ QLatin1String("QTextTableFormat"), QLatin1String("QtGui/qtextformat.h") },
{ QLatin1String("QThread"), QLatin1String("QtCore/qthread.h") },
{ QLatin1String("QThreadPool"), QLatin1String("QtCore/qthreadpool.h") },
{ QLatin1String("QTime"), QLatin1String("QtCore/qdatetime.h") },
{ QLatin1String("QTimeEdit"), QLatin1String("QtGui/qdatetimeedit.h") },
{ QLatin1String("QTimeLine"), QLatin1String("QtCore/qtimeline.h") },

View file

@ -1,3 +0,0 @@
katie_test(tst_qthreadpool
${CMAKE_CURRENT_SOURCE_DIR}/tst_qthreadpool.cpp
)

View file

@ -1,948 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Copyright (C) 2016 Ivailo Monev
**
** This file is part of the test suite of the Katie Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qdatetime.h>
#include <qthreadpool.h>
#include <qsemaphore.h>
#include <qstring.h>
#include <qmutex.h>
#include "../../shared/util.h"
typedef void (*FunctionPointer)();
class FunctionPointerTask : public QRunnable
{
public:
FunctionPointerTask(FunctionPointer function)
:function(function) {}
void run() { function(); }
private:
FunctionPointer function;
};
QRunnable *createTask(FunctionPointer pointer)
{
return new FunctionPointerTask(pointer);
}
class tst_QThreadPool : public QObject
{
Q_OBJECT
public:
tst_QThreadPool();
~tst_QThreadPool();
static QMutex *functionTestMutex;
private slots:
void runFunction();
void createThreadRunFunction();
void runMultiple();
void waitcomplete();
void runTask();
void singleton();
void destruction();
void threadRecycling();
void expiryTimeout();
void expiryTimeoutRace();
void exceptions();
void maxThreadCount();
void setMaxThreadCount_data();
void setMaxThreadCount();
void setMaxThreadCountStartsAndStopsThreads();
void activeThreadCount();
void reserveThread_data();
void reserveThread();
void releaseThread_data();
void releaseThread();
void reserveAndStart();
void start();
void tryStart();
void tryStartPeakThreadCount();
void tryStartCount();
void waitForDone();
void waitForDoneTimeout();
void destroyingWaitsForTasksToFinish();
void stressTest();
private:
QMutex m_functionTestMutex;
};
QMutex *tst_QThreadPool::functionTestMutex = 0;
tst_QThreadPool::tst_QThreadPool()
{
tst_QThreadPool::functionTestMutex = &m_functionTestMutex;
}
tst_QThreadPool::~tst_QThreadPool()
{
tst_QThreadPool::functionTestMutex = 0;
}
int testFunctionCount;
void sleepTestFunction()
{
QTest::qSleep(1000);
++testFunctionCount;
}
void emptyFunct()
{
}
void noSleepTestFunction()
{
++testFunctionCount;
}
void sleepTestFunctionMutex()
{
Q_ASSERT(tst_QThreadPool::functionTestMutex);
QTest::qSleep(1000);
tst_QThreadPool::functionTestMutex->lock();
++testFunctionCount;
tst_QThreadPool::functionTestMutex->unlock();
}
void noSleepTestFunctionMutex()
{
Q_ASSERT(tst_QThreadPool::functionTestMutex);
tst_QThreadPool::functionTestMutex->lock();
++testFunctionCount;
tst_QThreadPool::functionTestMutex->unlock();
}
void tst_QThreadPool::runFunction()
{
{
QThreadPool manager;
testFunctionCount = 0;
manager.start(createTask(noSleepTestFunction));
}
QCOMPARE(testFunctionCount, 1);
}
void tst_QThreadPool::createThreadRunFunction()
{
{
QThreadPool manager;
testFunctionCount = 0;
manager.start(createTask(noSleepTestFunction));
}
QCOMPARE(testFunctionCount, 1);
}
void tst_QThreadPool::runMultiple()
{
const int runs = 10;
{
QThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
manager.start(createTask(sleepTestFunctionMutex));
}
}
QCOMPARE(testFunctionCount, runs);
{
QThreadPool manager;
testFunctionCount = 0;
for (int i = 0; i < runs; ++i) {
manager.start(createTask(noSleepTestFunctionMutex));
}
}
QCOMPARE(testFunctionCount, runs);
{
QThreadPool manager;
for (int i = 0; i < 500; ++i)
manager.start(createTask(emptyFunct));
}
}
void tst_QThreadPool::waitcomplete()
{
testFunctionCount = 0;
const int runs = 500;
for (int i = 0; i < 500; ++i) {
QThreadPool pool;
pool.start(createTask(noSleepTestFunction));
}
QCOMPARE(testFunctionCount, runs);
}
QAtomicInt ran(0); // bool
class TestTask : public QRunnable
{
public:
void run()
{
ran = 1;
}
};
void tst_QThreadPool::runTask()
{
QThreadPool manager;
ran = 0;
manager.start(new TestTask());
// Hang if task is not runned.
while (ran == 0)
QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers
}
/*
Test running via QThreadPool::globalInstance()
*/
void tst_QThreadPool::singleton()
{
ran = 0;
QThreadPool::globalInstance()->start(new TestTask());
while (ran == 0)
QTest::qSleep(100); // no busy loop - this doesn't work with FIFO schedulers
}
QAtomicInt *value = 0;
class IntAccessor : public QRunnable
{
public:
void run()
{
for (int i = 0; i < 100; ++i) {
value->ref();
QTest::qSleep(1);
}
}
};
/*
Test that the ThreadManager destructor waits until
all threads have completed.
*/
void tst_QThreadPool::destruction()
{
value = new QAtomicInt(0);
QThreadPool *threadManager = new QThreadPool();
threadManager->start(new IntAccessor());
threadManager->start(new IntAccessor());
delete threadManager;
delete value;
}
QSemaphore threadRecyclingSemaphore;
QThread *recycledThread = 0;
class ThreadRecorderTask : public QRunnable
{
public:
void run()
{
recycledThread = QThread::currentThread();
threadRecyclingSemaphore.release();
}
};
/*
Test that the thread pool really reuses threads.
*/
void tst_QThreadPool::threadRecycling()
{
QThreadPool threadPool;
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
QThread *thread1 = recycledThread;
QTest::qSleep(100);
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
QThread *thread2 = recycledThread;
QCOMPARE(thread1, thread2);
QTest::qSleep(100);
threadPool.start(new ThreadRecorderTask());
threadRecyclingSemaphore.acquire();
QThread *thread3 = recycledThread;
QCOMPARE(thread2, thread3);
}
class ExpiryTimeoutTask : public QRunnable
{
public:
QThread *thread;
QAtomicInt runCount;
QSemaphore semaphore;
ExpiryTimeoutTask()
: thread(0), runCount(0)
{
setAutoDelete(false);
}
void run()
{
thread = QThread::currentThread();
runCount.ref();
semaphore.release();
}
};
void tst_QThreadPool::expiryTimeout()
{
ExpiryTimeoutTask task;
QThreadPool threadPool;
threadPool.setMaxThreadCount(1);
int expiryTimeout = threadPool.expiryTimeout();
threadPool.setExpiryTimeout(1000);
QCOMPARE(threadPool.expiryTimeout(), 1000);
// run the task
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
QCOMPARE(int(task.runCount), 1);
QVERIFY(!task.thread->wait(100));
// thread should expire
QThread *firstThread = task.thread;
QVERIFY(task.thread->wait(10000));
// run task again, thread should be restarted
threadPool.start(&task);
QVERIFY(task.semaphore.tryAcquire(1, 10000));
QCOMPARE(int(task.runCount), 2);
QVERIFY(!task.thread->wait(100));
// thread should expire again
QVERIFY(task.thread->wait(10000));
// thread pool should have reused the expired thread (instead of
// starting a new one)
QCOMPARE(firstThread, task.thread);
threadPool.setExpiryTimeout(expiryTimeout);
QCOMPARE(threadPool.expiryTimeout(), expiryTimeout);
}
void tst_QThreadPool::expiryTimeoutRace() // QTBUG-3786
{
ExpiryTimeoutTask task;
QThreadPool threadPool;
threadPool.setMaxThreadCount(1);
threadPool.setExpiryTimeout(50);
const int numTasks = 20;
for (int i = 0; i < numTasks; ++i) {
threadPool.start(&task);
QTest::qSleep(50); // exactly the same as the expiry timeout
}
QCOMPARE(int(task.runCount), numTasks);
QVERIFY(threadPool.waitForDone(2000));
}
#ifndef QT_NO_EXCEPTIONS
class ExceptionTask : public QRunnable
{
public:
void run()
{
throw new int;
}
};
#endif
void tst_QThreadPool::exceptions()
{
#ifndef QT_NO_EXCEPTIONS
ExceptionTask task;
{
QThreadPool threadPool;
// Uncomment this for a nice crash.
// threadPool.start(&task);
}
#else
QSKIP("No exception support", SkipAll);
#endif
}
void tst_QThreadPool::maxThreadCount()
{
DEPENDS_ON("setMaxThreadCount()");
}
void tst_QThreadPool::setMaxThreadCount_data()
{
QTest::addColumn<int>("limit");
QTest::newRow("") << 1;
QTest::newRow("") << -1;
QTest::newRow("") << 2;
QTest::newRow("") << -2;
QTest::newRow("") << 4;
QTest::newRow("") << -4;
QTest::newRow("") << 0;
QTest::newRow("") << 12345;
QTest::newRow("") << -6789;
QTest::newRow("") << 42;
QTest::newRow("") << -666;
}
void tst_QThreadPool::setMaxThreadCount()
{
QFETCH(int, limit);
QThreadPool *threadPool = QThreadPool::globalInstance();
int savedLimit = threadPool->maxThreadCount();
// maxThreadCount() should always return the previous argument to
// setMaxThreadCount(), regardless of input
threadPool->setMaxThreadCount(limit);
QCOMPARE(threadPool->maxThreadCount(), limit);
// the value returned from maxThreadCount() should always be valid input for setMaxThreadCount()
threadPool->setMaxThreadCount(savedLimit);
QCOMPARE(threadPool->maxThreadCount(), savedLimit);
// setting the limit on children should have no effect on the parent
{
QThreadPool threadPool2(threadPool);
savedLimit = threadPool2.maxThreadCount();
// maxThreadCount() should always return the previous argument to
// setMaxThreadCount(), regardless of input
threadPool2.setMaxThreadCount(limit);
QCOMPARE(threadPool2.maxThreadCount(), limit);
// the value returned from maxThreadCount() should always be valid input for setMaxThreadCount()
threadPool2.setMaxThreadCount(savedLimit);
QCOMPARE(threadPool2.maxThreadCount(), savedLimit);
}
}
void tst_QThreadPool::setMaxThreadCountStartsAndStopsThreads()
{
class WaitingTask : public QRunnable
{
public:
QSemaphore waitForStarted, waitToFinish;
WaitingTask() { setAutoDelete(false); }
void run()
{
waitForStarted.release();
waitToFinish.acquire();
}
};
QThreadPool threadPool;
threadPool.setMaxThreadCount(1);
WaitingTask *task = new WaitingTask;
threadPool.start(task);
QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
// thread limit is 1, cannot start more tasks
threadPool.start(task);
QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
// increasing the limit by 1 should start the task immediately
threadPool.setMaxThreadCount(2);
QVERIFY(task->waitForStarted.tryAcquire(1, 1000));
// ... but we still cannot start more tasks
threadPool.start(task);
QVERIFY(!task->waitForStarted.tryAcquire(1, 1000));
// increasing the limit should be able to start more than one at a time
threadPool.start(task);
threadPool.setMaxThreadCount(4);
QVERIFY(task->waitForStarted.tryAcquire(2, 1000));
// ... but we still cannot start more tasks
threadPool.start(task);
threadPool.start(task);
QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
// decreasing the thread limit should cause the active thread count to go down
threadPool.setMaxThreadCount(2);
QCOMPARE(threadPool.activeThreadCount(), 4);
task->waitToFinish.release(2);
QTest::qWait(1000);
QCOMPARE(threadPool.activeThreadCount(), 2);
// ... and we still cannot start more tasks
threadPool.start(task);
threadPool.start(task);
QVERIFY(!task->waitForStarted.tryAcquire(2, 1000));
// start all remaining tasks
threadPool.start(task);
threadPool.start(task);
threadPool.start(task);
threadPool.start(task);
threadPool.setMaxThreadCount(8);
QVERIFY(task->waitForStarted.tryAcquire(6, 1000));
task->waitToFinish.release(10);
// delete task;
}
void tst_QThreadPool::activeThreadCount()
{
DEPENDS_ON("tryReserveThread()");
DEPENDS_ON("reserveThread()");
DEPENDS_ON("releaseThread()");
}
void tst_QThreadPool::reserveThread_data()
{
setMaxThreadCount_data();
}
void tst_QThreadPool::reserveThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
int savedLimit = threadpool->maxThreadCount();
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
for (int i = 0; i < limit; ++i)
threadpool->reserveThread();
// reserveThread() should always reserve a thread, regardless of
// how many have been previously reserved
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 1);
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), (limit > 0 ? limit : 0) + 2);
// cleanup
threadpool->releaseThread();
threadpool->releaseThread();
for (int i = 0; i < limit; ++i)
threadpool->releaseThread();
// reserving threads in children should not effect the parent
{
QThreadPool threadpool2(threadpool);
threadpool2.setMaxThreadCount(limit);
// reserve up to the limit
for (int i = 0; i < limit; ++i)
threadpool2.reserveThread();
// reserveThread() should always reserve a thread, regardless
// of how many have been previously reserved
threadpool2.reserveThread();
QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 1);
threadpool2.reserveThread();
QCOMPARE(threadpool2.activeThreadCount(), (limit > 0 ? limit : 0) + 2);
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 1);
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 2);
// cleanup
threadpool2.releaseThread();
threadpool2.releaseThread();
threadpool->releaseThread();
threadpool->releaseThread();
while (threadpool2.activeThreadCount() > 0)
threadpool2.releaseThread();
}
// reset limit on global QThreadPool
threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::releaseThread_data()
{
setMaxThreadCount_data();
}
void tst_QThreadPool::releaseThread()
{
QFETCH(int, limit);
QThreadPool *threadpool = QThreadPool::globalInstance();
int savedLimit = threadpool->maxThreadCount();
threadpool->setMaxThreadCount(limit);
// reserve up to the limit
for (int i = 0; i < limit; ++i)
threadpool->reserveThread();
// release should decrease the number of reserved threads
int reserved = threadpool->activeThreadCount();
while (reserved-- > 0) {
threadpool->releaseThread();
QCOMPARE(threadpool->activeThreadCount(), reserved);
}
QCOMPARE(threadpool->activeThreadCount(), 0);
// releaseThread() can release more than have been reserved
threadpool->releaseThread();
QCOMPARE(threadpool->activeThreadCount(), -1);
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 0);
// releasing threads in children should not effect the parent
{
QThreadPool threadpool2(threadpool);
threadpool2.setMaxThreadCount(limit);
// reserve up to the limit
for (int i = 0; i < limit; ++i)
threadpool2.reserveThread();
// release should decrease the number of reserved threads
int reserved = threadpool2.activeThreadCount();
while (reserved-- > 0) {
threadpool2.releaseThread();
QCOMPARE(threadpool2.activeThreadCount(), reserved);
QCOMPARE(threadpool->activeThreadCount(), 0);
}
QCOMPARE(threadpool2.activeThreadCount(), 0);
QCOMPARE(threadpool->activeThreadCount(), 0);
// releaseThread() can release more than have been reserved
threadpool2.releaseThread();
QCOMPARE(threadpool2.activeThreadCount(), -1);
QCOMPARE(threadpool->activeThreadCount(), 0);
threadpool2.reserveThread();
QCOMPARE(threadpool2.activeThreadCount(), 0);
QCOMPARE(threadpool->activeThreadCount(), 0);
}
// reset limit on global QThreadPool
threadpool->setMaxThreadCount(savedLimit);
}
void tst_QThreadPool::reserveAndStart() // QTBUG-21051
{
class WaitingTask : public QRunnable
{
public:
QAtomicInt count;
QSemaphore waitForStarted;
WaitingTask() { setAutoDelete(false); }
void run()
{
QTest::qSleep(3000);
count.ref();
waitForStarted.release();
}
};
// Set up
QThreadPool *threadpool = QThreadPool::globalInstance();
int savedLimit = threadpool->maxThreadCount();
threadpool->setMaxThreadCount(1);
QCOMPARE(threadpool->activeThreadCount(), 0);
// reserve
threadpool->reserveThread();
QCOMPARE(threadpool->activeThreadCount(), 1);
// start a task, to get a running thread
WaitingTask *task = new WaitingTask;
threadpool->start(task);
QCOMPARE(threadpool->activeThreadCount(), 2);
task->waitForStarted.acquire();
QTRY_COMPARE(int(task->count), 1);
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
// now the thread is waiting, but tryStart() will fail since activeThreadCount() >= maxThreadCount()
QVERIFY(!threadpool->tryStart(task));
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
// start() will therefore do a failing tryStart(), followed by enqueueTask()
// which will actually wake up the waiting thread.
threadpool->start(task);
QTRY_COMPARE(threadpool->activeThreadCount(), 2);
task->waitForStarted.acquire();
QTRY_COMPARE(int(task->count), 2);
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
threadpool->releaseThread();
QTRY_COMPARE(threadpool->activeThreadCount(), 0);
delete task;
threadpool->setMaxThreadCount(savedLimit);
}
QAtomicInt count;
class CountingRunnable : public QRunnable
{
public: void run()
{
count.ref();
}
};
void tst_QThreadPool::start()
{
const int runs = 1000;
count = 0;
{
QThreadPool threadPool;
for (int i = 0; i< runs; ++i) {
threadPool.start(new CountingRunnable());
}
}
QCOMPARE(int(count), runs);
}
void tst_QThreadPool::tryStart()
{
class WaitingTask : public QRunnable
{
public:
QSemaphore semaphore;
WaitingTask() { setAutoDelete(false); }
void run()
{
semaphore.acquire();
count.ref();
}
};
count = 0;
WaitingTask task;
QThreadPool threadPool;
for (int i = 0; i < threadPool.maxThreadCount(); ++i) {
threadPool.start(&task);
}
QVERIFY(!threadPool.tryStart(&task));
task.semaphore.release(threadPool.maxThreadCount());
threadPool.waitForDone();
QCOMPARE(int(count), threadPool.maxThreadCount());
}
QMutex mutex;
QAtomicInt activeThreads(0);
QAtomicInt peakActiveThreads(0);
void tst_QThreadPool::tryStartPeakThreadCount()
{
class CounterTask : public QRunnable
{
public:
CounterTask() { setAutoDelete(false); }
void run()
{
{
QMutexLocker lock(&mutex);
activeThreads.ref();
peakActiveThreads = qMax(peakActiveThreads, activeThreads);
}
QTest::qWait(100);
{
QMutexLocker lock(&mutex);
activeThreads.deref();
}
}
};
CounterTask task;
QThreadPool threadPool;
for (int i = 0; i < 20; ++i) {
if (threadPool.tryStart(&task) == false)
QTest::qWait(10);
}
QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount());
for (int i = 0; i < 20; ++i) {
if (threadPool.tryStart(&task) == false)
QTest::qWait(10);
}
QCOMPARE(peakActiveThreads.load(), QThread::idealThreadCount());
}
void tst_QThreadPool::tryStartCount()
{
class SleeperTask : public QRunnable
{
public:
SleeperTask() { setAutoDelete(false); }
void run()
{
QTest::qWait(50);
}
};
SleeperTask task;
QThreadPool threadPool;
const int runs = 5;
for (int i = 0; i < runs; ++i) {
// qDebug() << "iteration" << i;
int count = 0;
while (threadPool.tryStart(&task))
++count;
QCOMPARE(count, QThread::idealThreadCount());
QTest::qWait(100);
}
}
void tst_QThreadPool::waitForDone()
{
QElapsedTimer total, pass;
total.start();
QThreadPool threadPool;
while (total.elapsed() < 10000) {
int runs;
runs = count = 0;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
++runs;
}
threadPool.waitForDone();
QCOMPARE(int(count), runs);
runs = count = 0;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
threadPool.start(new CountingRunnable());
runs += 2;
}
threadPool.waitForDone();
QCOMPARE(int(count), runs);
}
}
void tst_QThreadPool::waitForDoneTimeout()
{
class BlockedTask : public QRunnable
{
public:
QMutex mutex;
BlockedTask() { setAutoDelete(false); }
void run()
{
mutex.lock();
mutex.unlock();
QTest::qSleep(50);
}
};
QThreadPool threadPool;
BlockedTask *task = new BlockedTask;
task->mutex.lock();
threadPool.start(task);
QVERIFY(!threadPool.waitForDone(100));
task->mutex.unlock();
QVERIFY(threadPool.waitForDone(400));
}
void tst_QThreadPool::destroyingWaitsForTasksToFinish()
{
QElapsedTimer total, pass;
total.start();
while (total.elapsed() < 10000) {
int runs;
runs = count = 0;
{
QThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
++runs;
}
}
QCOMPARE(int(count), runs);
runs = count = 0;
{
QThreadPool threadPool;
pass.restart();
while (pass.elapsed() < 100) {
threadPool.start(new CountingRunnable());
threadPool.start(new CountingRunnable());
runs += 2;
}
}
QCOMPARE(int(count), runs);
}
}
void tst_QThreadPool::stressTest()
{
class Task : public QRunnable
{
QSemaphore semaphore;
public:
Task() { setAutoDelete(false); }
void start()
{
QThreadPool::globalInstance()->start(this);
}
void wait()
{
semaphore.acquire();
}
void run()
{
semaphore.release();
}
};
QElapsedTimer total;
total.start();
while (total.elapsed() < 30000) {
Task t;
t.start();
t.wait();
}
}
QTEST_MAIN(tst_QThreadPool);
#include "moc_tst_qthreadpool.cpp"