Fix QRunnable::ref use in QThreadPool

The reference counter could only ever be -1, 0 or 1,
as an autoDeleted QRunnable can only be in a single
thread pool.

This commits keeps the reference counting for now,
but asserts sanity, simplifies locking and fixes a
leak.

Pick-To: 5.15
Fixes: QTBUG-20251
Fixes: QTBUG-65486
Change-Id: I4de44e9a4e3710225971d1eab8f2e332513f75ad
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
(cherry picked from commit 891b60bbc8)
This commit is contained in:
Allan Sandfeld Jensen 2020-01-28 15:55:07 +01:00
parent 8191472bc8
commit 5a4b275a20
2 changed files with 43 additions and 25 deletions

View File

@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QRunnable
{
int ref;
int ref; // Qt6: Make this a bool, or make autoDelete() virtual.
friend class QThreadPool;
friend class QThreadPoolPrivate;

View File

@ -88,7 +88,8 @@ void QThreadPoolThread::run()
do {
if (r) {
const bool autoDelete = r->autoDelete();
const bool del = r->autoDelete();
Q_ASSERT(!del || r->ref == 1);
// run the task
@ -106,10 +107,10 @@ void QThreadPoolThread::run()
throw;
}
#endif
locker.relock();
if (autoDelete && !--r->ref)
if (del)
delete r;
locker.relock();
}
// if too many threads are active, expire this thread
@ -193,8 +194,6 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
++activeThreads;
if (task->autoDelete())
++task->ref;
thread->runnable = task;
thread->start();
return true;
@ -213,9 +212,6 @@ inline bool comparePriority(int priority, const QueuePage *p)
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
{
Q_ASSERT(runnable != nullptr);
if (runnable->autoDelete())
++runnable->ref;
for (QueuePage *page : qAsConst(queue)) {
if (page->priority() == priority && !page->isFull()) {
page->push(runnable);
@ -269,8 +265,6 @@ void QThreadPoolPrivate::startThread(QRunnable *runnable)
allThreads.insert(thread.data());
++activeThreads;
if (runnable->autoDelete())
++runnable->ref;
thread->runnable = runnable;
thread.take()->start();
}
@ -334,8 +328,12 @@ void QThreadPoolPrivate::clear()
for (QueuePage *page : qAsConst(queue)) {
while (!page->isFinished()) {
QRunnable *r = page->pop();
if (r && r->autoDelete() && !--r->ref)
if (r && r->autoDelete()) {
Q_ASSERT(r->ref == 1);
locker.unlock();
delete r;
locker.relock();
}
}
}
qDeleteAll(queue);
@ -365,19 +363,19 @@ bool QThreadPool::tryTake(QRunnable *runnable)
if (runnable == nullptr)
return false;
{
QMutexLocker locker(&d->mutex);
for (QueuePage *page : qAsConst(d->queue)) {
if (page->tryTake(runnable)) {
if (page->isFinished()) {
d->queue.removeOne(page);
delete page;
}
if (runnable->autoDelete())
--runnable->ref; // undo ++ref in start()
return true;
QMutexLocker locker(&d->mutex);
for (QueuePage *page : qAsConst(d->queue)) {
if (page->tryTake(runnable)) {
if (page->isFinished()) {
d->queue.removeOne(page);
delete page;
}
if (runnable->autoDelete()) {
Q_ASSERT(runnable->ref == 1);
--runnable->ref; // undo ++ref in start()
}
return true;
}
}
@ -395,11 +393,12 @@ void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable)
Q_Q(QThreadPool);
if (!q->tryTake(runnable))
return;
const bool del = runnable->autoDelete() && !runnable->ref; // tryTake already deref'ed
const bool del = runnable->autoDelete();
runnable->run();
if (del) {
Q_ASSERT(runnable->ref == 0); // tryTake already deref'ed
delete runnable;
}
}
@ -503,6 +502,11 @@ void QThreadPool::start(QRunnable *runnable, int priority)
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
if (runnable->autoDelete()) {
Q_ASSERT(runnable->ref == 0);
++runnable->ref;
}
if (!d->tryStart(runnable)) {
d->enqueueTask(runnable, priority);
@ -548,9 +552,23 @@ bool QThreadPool::tryStart(QRunnable *runnable)
if (!runnable)
return false;
if (runnable->autoDelete()) {
Q_ASSERT(runnable->ref == 0);
++runnable->ref;
}
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
return d->tryStart(runnable);
if (d->tryStart(runnable))
return true;
// Undo the reference above as we did not start the runnable and
// take over ownership.
if (runnable->autoDelete()) {
--runnable->ref;
Q_ASSERT(runnable->ref == 0);
}
return false;
}
/*!