Fix UB (data race) in tst_QThreadPool::cancel()
Manipulating a simple int from multiple threads is a data race, thus undefined behavior. Fix by using QAtomicInt and atomic operations instead. Change-Id: I5418bc260da57fe353a71b8e5c7c1c97adbe7597 Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
parent
af8771867c
commit
dcf74bdec8
@ -962,14 +962,21 @@ void tst_QThreadPool::cancel()
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QSemaphore & sem;
|
QSemaphore & sem;
|
||||||
int & dtorCounter;
|
QAtomicInt &dtorCounter;
|
||||||
int & runCounter;
|
QAtomicInt &runCounter;
|
||||||
int dummy;
|
int dummy;
|
||||||
BlockingRunnable(QSemaphore & s, int & c, int & r) : sem(s), dtorCounter(c), runCounter(r){}
|
|
||||||
~BlockingRunnable(){dtorCounter++;}
|
explicit BlockingRunnable(QSemaphore &s, QAtomicInt &c, QAtomicInt &r)
|
||||||
|
: sem(s), dtorCounter(c), runCounter(r){}
|
||||||
|
|
||||||
|
~BlockingRunnable()
|
||||||
|
{
|
||||||
|
dtorCounter.fetchAndAddRelaxed(1);
|
||||||
|
}
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
runCounter++;
|
runCounter.fetchAndAddRelaxed(1);
|
||||||
sem.acquire();
|
sem.acquire();
|
||||||
count.ref();
|
count.ref();
|
||||||
}
|
}
|
||||||
@ -981,8 +988,8 @@ void tst_QThreadPool::cancel()
|
|||||||
int runs = 2 * threadPool.maxThreadCount();
|
int runs = 2 * threadPool.maxThreadCount();
|
||||||
BlockingRunnablePtr* runnables = new BlockingRunnablePtr[runs];
|
BlockingRunnablePtr* runnables = new BlockingRunnablePtr[runs];
|
||||||
count.store(0);
|
count.store(0);
|
||||||
int dtorCounter = 0;
|
QAtomicInt dtorCounter = 0;
|
||||||
int runCounter = 0;
|
QAtomicInt runCounter = 0;
|
||||||
for (int i = 0; i < runs; i++) {
|
for (int i = 0; i < runs; i++) {
|
||||||
runnables[i] = new BlockingRunnable(sem, dtorCounter, runCounter);
|
runnables[i] = new BlockingRunnable(sem, dtorCounter, runCounter);
|
||||||
runnables[i]->setAutoDelete(i != 0 && i != (runs-1)); //one which will run and one which will not
|
runnables[i]->setAutoDelete(i != 0 && i != (runs-1)); //one which will run and one which will not
|
||||||
@ -994,12 +1001,12 @@ void tst_QThreadPool::cancel()
|
|||||||
}
|
}
|
||||||
runnables[0]->dummy = 0; //valgrind will catch this if cancel() is crazy enough to delete currently running jobs
|
runnables[0]->dummy = 0; //valgrind will catch this if cancel() is crazy enough to delete currently running jobs
|
||||||
runnables[runs-1]->dummy = 0;
|
runnables[runs-1]->dummy = 0;
|
||||||
QCOMPARE(dtorCounter, runs-threadPool.maxThreadCount()-1);
|
QCOMPARE(dtorCounter.load(), runs - threadPool.maxThreadCount() - 1);
|
||||||
sem.release(threadPool.maxThreadCount());
|
sem.release(threadPool.maxThreadCount());
|
||||||
threadPool.waitForDone();
|
threadPool.waitForDone();
|
||||||
QCOMPARE(runCounter, threadPool.maxThreadCount());
|
QCOMPARE(runCounter.load(), threadPool.maxThreadCount());
|
||||||
QCOMPARE(count.load(), threadPool.maxThreadCount());
|
QCOMPARE(count.load(), threadPool.maxThreadCount());
|
||||||
QCOMPARE(dtorCounter, runs-2);
|
QCOMPARE(dtorCounter.load(), runs - 2);
|
||||||
delete runnables[0]; //if the pool deletes them then we'll get double-free crash
|
delete runnables[0]; //if the pool deletes them then we'll get double-free crash
|
||||||
delete runnables[runs-1];
|
delete runnables[runs-1];
|
||||||
delete[] runnables;
|
delete[] runnables;
|
||||||
|
Loading…
Reference in New Issue
Block a user