Fix race condition in QThreadPool::clear
Since we drop the lock while deleting threads, we need to handle the queue possibly being accessed and changed by the pool threads while clear() is running. Pick-to: 5.15 Fixes: QTBUG-87092 Change-Id: I7611edab90520454278502a58621e299f9cd1f6e Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
This commit is contained in:
parent
4d943225eb
commit
fe36d47b37
@ -325,7 +325,8 @@ bool QThreadPoolPrivate::waitForDone(int msecs)
|
|||||||
void QThreadPoolPrivate::clear()
|
void QThreadPoolPrivate::clear()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&mutex);
|
QMutexLocker locker(&mutex);
|
||||||
for (QueuePage *page : qAsConst(queue)) {
|
while (!queue.isEmpty()) {
|
||||||
|
auto *page = queue.takeLast();
|
||||||
while (!page->isFinished()) {
|
while (!page->isFinished()) {
|
||||||
QRunnable *r = page->pop();
|
QRunnable *r = page->pop();
|
||||||
if (r && r->autoDelete()) {
|
if (r && r->autoDelete()) {
|
||||||
@ -335,9 +336,8 @@ void QThreadPoolPrivate::clear()
|
|||||||
locker.relock();
|
locker.relock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete page;
|
||||||
}
|
}
|
||||||
qDeleteAll(queue);
|
|
||||||
queue.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -93,6 +93,7 @@ private slots:
|
|||||||
void priorityStart();
|
void priorityStart();
|
||||||
void waitForDone();
|
void waitForDone();
|
||||||
void clear();
|
void clear();
|
||||||
|
void clearWithAutoDelete();
|
||||||
void tryTake();
|
void tryTake();
|
||||||
void waitForDoneTimeout();
|
void waitForDoneTimeout();
|
||||||
void destroyingWaitsForTasksToFinish();
|
void destroyingWaitsForTasksToFinish();
|
||||||
@ -974,6 +975,31 @@ void tst_QThreadPool::clear()
|
|||||||
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
|
QCOMPARE(count.loadRelaxed(), threadPool.maxThreadCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QThreadPool::clearWithAutoDelete()
|
||||||
|
{
|
||||||
|
class MyRunnable : public QRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyRunnable() {}
|
||||||
|
void run() override { QThread::usleep(30); }
|
||||||
|
};
|
||||||
|
|
||||||
|
QThreadPool threadPool;
|
||||||
|
threadPool.setMaxThreadCount(4);
|
||||||
|
const int loopCount = 20;
|
||||||
|
const int batchSize = 500;
|
||||||
|
// Should not crash see QTBUG-87092
|
||||||
|
for (int i = 0; i < loopCount; i++) {
|
||||||
|
threadPool.clear();
|
||||||
|
for (int j = 0; j < batchSize; j++) {
|
||||||
|
auto *runnable = new MyRunnable();
|
||||||
|
runnable->setAutoDelete(true);
|
||||||
|
threadPool.start(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QVERIFY(threadPool.waitForDone());
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QThreadPool::tryTake()
|
void tst_QThreadPool::tryTake()
|
||||||
{
|
{
|
||||||
QSemaphore sem(0);
|
QSemaphore sem(0);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user