summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-28 15:55:07 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-05-07 13:00:30 +0000
commit891b60bbc84ddde077072df3426539c716d47459 (patch)
tree79709efb0c9c9df2d65b8ffd4996c0a754189552
parent9b4b406142b05eef8ef255561f24951f25567a8e (diff)
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>
-rw-r--r--src/corelib/thread/qrunnable.h2
-rw-r--r--src/corelib/thread/qthreadpool.cpp68
2 files changed, 44 insertions, 26 deletions
diff --git a/src/corelib/thread/qrunnable.h b/src/corelib/thread/qrunnable.h
index 26b6b991ac..47ea00bdb0 100644
--- a/src/corelib/thread/qrunnable.h
+++ b/src/corelib/thread/qrunnable.h
@@ -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;
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index 44cdf071df..1f99bad247 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -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;
}
/*!