QThreadPool: fix counting of waiting threads
QTBUG-21051 has a testcase where activeThreadCount() could actually end up at -1 (converted to an autotest in this commit). The reason was: start() calls tryStart() which returns false due to too many active threads (reserveThread() causes this), so it calls enqueueTask() - which actually wakes up the waiting thread, but it didn't decrement the number of waiting threads. Note that tryStart() is "if I can grab a waiting thread, enqueue task and wake it" while start(), in case tryStart() fails, wants to "enqueue, and then if I can grab a waiting thread, wake it". This is why enqueue shouldn't wake; waking must happen only if we can grab a thread (d->waitingThreads > 0). Task-number: QTBUG-21051 Change-Id: I3d98337103031c9bdf0bf365295f245be0c66aa7 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
7c2a418857
commit
dacf9961da
@ -180,6 +180,7 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
|
||||
// recycle an available thread
|
||||
--waitingThreads;
|
||||
enqueueTask(task);
|
||||
runnableReady.wakeOne();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -218,7 +219,6 @@ void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
|
||||
if (it != begin && priority > (*(it - 1)).second)
|
||||
it = std::upper_bound(begin, --it, priority);
|
||||
queue.insert(it - begin, qMakePair(runnable, priority));
|
||||
runnableReady.wakeOne();
|
||||
}
|
||||
|
||||
int QThreadPoolPrivate::activeThreadCount() const
|
||||
@ -456,8 +456,14 @@ void QThreadPool::start(QRunnable *runnable, int priority)
|
||||
|
||||
Q_D(QThreadPool);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
if (!d->tryStart(runnable))
|
||||
if (!d->tryStart(runnable)) {
|
||||
d->enqueueTask(runnable, priority);
|
||||
|
||||
if (d->waitingThreads > 0) {
|
||||
--d->waitingThreads;
|
||||
d->runnableReady.wakeOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -90,6 +90,7 @@ private slots:
|
||||
void reserveThread();
|
||||
void releaseThread_data();
|
||||
void releaseThread();
|
||||
void reserveAndStart();
|
||||
void start();
|
||||
void tryStart();
|
||||
void tryStartPeakThreadCount();
|
||||
@ -630,6 +631,61 @@ void tst_QThreadPool::releaseThread()
|
||||
threadpool->setMaxThreadCount(savedLimit);
|
||||
}
|
||||
|
||||
void tst_QThreadPool::reserveAndStart() // QTBUG-21051
|
||||
{
|
||||
class WaitingTask : public QRunnable
|
||||
{
|
||||
public:
|
||||
QAtomicInt count;
|
||||
QSemaphore waitForStarted;
|
||||
|
||||
WaitingTask() { setAutoDelete(false); }
|
||||
|
||||
void run()
|
||||
{
|
||||
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(task->count.load(), 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(task->count.load(), 2);
|
||||
QTRY_COMPARE(threadpool->activeThreadCount(), 1);
|
||||
|
||||
threadpool->releaseThread();
|
||||
QTRY_COMPARE(threadpool->activeThreadCount(), 0);
|
||||
|
||||
delete task;
|
||||
|
||||
threadpool->setMaxThreadCount(savedLimit);
|
||||
}
|
||||
|
||||
QAtomicInt count;
|
||||
class CountingRunnable : public QRunnable
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user