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>
This commit is contained in:
parent
9b4b406142
commit
891b60bbc8
@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
class Q_CORE_EXPORT QRunnable
|
class Q_CORE_EXPORT QRunnable
|
||||||
{
|
{
|
||||||
int ref;
|
int ref; // Qt6: Make this a bool, or make autoDelete() virtual.
|
||||||
|
|
||||||
friend class QThreadPool;
|
friend class QThreadPool;
|
||||||
friend class QThreadPoolPrivate;
|
friend class QThreadPoolPrivate;
|
||||||
|
@ -88,7 +88,8 @@ void QThreadPoolThread::run()
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if (r) {
|
if (r) {
|
||||||
const bool autoDelete = r->autoDelete();
|
const bool del = r->autoDelete();
|
||||||
|
Q_ASSERT(!del || r->ref == 1);
|
||||||
|
|
||||||
|
|
||||||
// run the task
|
// run the task
|
||||||
@ -106,10 +107,10 @@ void QThreadPoolThread::run()
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
locker.relock();
|
|
||||||
|
|
||||||
if (autoDelete && !--r->ref)
|
if (del)
|
||||||
delete r;
|
delete r;
|
||||||
|
locker.relock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if too many threads are active, expire this thread
|
// if too many threads are active, expire this thread
|
||||||
@ -193,8 +194,6 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
|
|||||||
|
|
||||||
++activeThreads;
|
++activeThreads;
|
||||||
|
|
||||||
if (task->autoDelete())
|
|
||||||
++task->ref;
|
|
||||||
thread->runnable = task;
|
thread->runnable = task;
|
||||||
thread->start();
|
thread->start();
|
||||||
return true;
|
return true;
|
||||||
@ -213,9 +212,6 @@ inline bool comparePriority(int priority, const QueuePage *p)
|
|||||||
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
|
void QThreadPoolPrivate::enqueueTask(QRunnable *runnable, int priority)
|
||||||
{
|
{
|
||||||
Q_ASSERT(runnable != nullptr);
|
Q_ASSERT(runnable != nullptr);
|
||||||
if (runnable->autoDelete())
|
|
||||||
++runnable->ref;
|
|
||||||
|
|
||||||
for (QueuePage *page : qAsConst(queue)) {
|
for (QueuePage *page : qAsConst(queue)) {
|
||||||
if (page->priority() == priority && !page->isFull()) {
|
if (page->priority() == priority && !page->isFull()) {
|
||||||
page->push(runnable);
|
page->push(runnable);
|
||||||
@ -269,8 +265,6 @@ void QThreadPoolPrivate::startThread(QRunnable *runnable)
|
|||||||
allThreads.insert(thread.data());
|
allThreads.insert(thread.data());
|
||||||
++activeThreads;
|
++activeThreads;
|
||||||
|
|
||||||
if (runnable->autoDelete())
|
|
||||||
++runnable->ref;
|
|
||||||
thread->runnable = runnable;
|
thread->runnable = runnable;
|
||||||
thread.take()->start();
|
thread.take()->start();
|
||||||
}
|
}
|
||||||
@ -334,8 +328,12 @@ void QThreadPoolPrivate::clear()
|
|||||||
for (QueuePage *page : qAsConst(queue)) {
|
for (QueuePage *page : qAsConst(queue)) {
|
||||||
while (!page->isFinished()) {
|
while (!page->isFinished()) {
|
||||||
QRunnable *r = page->pop();
|
QRunnable *r = page->pop();
|
||||||
if (r && r->autoDelete() && !--r->ref)
|
if (r && r->autoDelete()) {
|
||||||
|
Q_ASSERT(r->ref == 1);
|
||||||
|
locker.unlock();
|
||||||
delete r;
|
delete r;
|
||||||
|
locker.relock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDeleteAll(queue);
|
qDeleteAll(queue);
|
||||||
@ -365,19 +363,19 @@ bool QThreadPool::tryTake(QRunnable *runnable)
|
|||||||
|
|
||||||
if (runnable == nullptr)
|
if (runnable == nullptr)
|
||||||
return false;
|
return false;
|
||||||
{
|
|
||||||
QMutexLocker locker(&d->mutex);
|
|
||||||
|
|
||||||
for (QueuePage *page : qAsConst(d->queue)) {
|
QMutexLocker locker(&d->mutex);
|
||||||
if (page->tryTake(runnable)) {
|
for (QueuePage *page : qAsConst(d->queue)) {
|
||||||
if (page->isFinished()) {
|
if (page->tryTake(runnable)) {
|
||||||
d->queue.removeOne(page);
|
if (page->isFinished()) {
|
||||||
delete page;
|
d->queue.removeOne(page);
|
||||||
}
|
delete page;
|
||||||
if (runnable->autoDelete())
|
|
||||||
--runnable->ref; // undo ++ref in start()
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
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);
|
Q_Q(QThreadPool);
|
||||||
if (!q->tryTake(runnable))
|
if (!q->tryTake(runnable))
|
||||||
return;
|
return;
|
||||||
const bool del = runnable->autoDelete() && !runnable->ref; // tryTake already deref'ed
|
const bool del = runnable->autoDelete();
|
||||||
|
|
||||||
runnable->run();
|
runnable->run();
|
||||||
|
|
||||||
if (del) {
|
if (del) {
|
||||||
|
Q_ASSERT(runnable->ref == 0); // tryTake already deref'ed
|
||||||
delete runnable;
|
delete runnable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -503,6 +502,11 @@ void QThreadPool::start(QRunnable *runnable, int priority)
|
|||||||
|
|
||||||
Q_D(QThreadPool);
|
Q_D(QThreadPool);
|
||||||
QMutexLocker locker(&d->mutex);
|
QMutexLocker locker(&d->mutex);
|
||||||
|
if (runnable->autoDelete()) {
|
||||||
|
Q_ASSERT(runnable->ref == 0);
|
||||||
|
++runnable->ref;
|
||||||
|
}
|
||||||
|
|
||||||
if (!d->tryStart(runnable)) {
|
if (!d->tryStart(runnable)) {
|
||||||
d->enqueueTask(runnable, priority);
|
d->enqueueTask(runnable, priority);
|
||||||
|
|
||||||
@ -548,9 +552,23 @@ bool QThreadPool::tryStart(QRunnable *runnable)
|
|||||||
if (!runnable)
|
if (!runnable)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (runnable->autoDelete()) {
|
||||||
|
Q_ASSERT(runnable->ref == 0);
|
||||||
|
++runnable->ref;
|
||||||
|
}
|
||||||
|
|
||||||
Q_D(QThreadPool);
|
Q_D(QThreadPool);
|
||||||
QMutexLocker locker(&d->mutex);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
x
Reference in New Issue
Block a user