mirror of
https://bitbucket.org/smil3y/katie.git
synced 2025-02-23 18:32:55 +00:00
drop QThreadPool and QRunnable
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
cd3d86140e
commit
b2504b1717
12 changed files with 0 additions and 1836 deletions
|
@ -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
|
||||
|
|
|
@ -350,7 +350,6 @@ classlist = [
|
|||
"QReturnArgument",
|
||||
"QRgb",
|
||||
"QRubberBand",
|
||||
"QRunnable",
|
||||
"QScopedPointer",
|
||||
"QScopedPointerPodDeleter",
|
||||
"QScopedValueRollback",
|
||||
|
@ -493,7 +492,6 @@ classlist = [
|
|||
"QTextTableCellFormat",
|
||||
"QTextTableFormat",
|
||||
"QThread",
|
||||
"QThreadPool",
|
||||
"QTileRules",
|
||||
"QTime",
|
||||
"QTimeEdit",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
*/
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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") },
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
katie_test(tst_qthreadpool
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qthreadpool.cpp
|
||||
)
|
|
@ -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"
|
Loading…
Add table
Reference in a new issue