diff options
-rw-r--r-- | src/corelib/thread/qfutureinterface.cpp | 4 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.cpp | 44 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.h | 1 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool_p.h | 3 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp | 51 |
5 files changed, 91 insertions, 12 deletions
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp index 1f417d3d74..a23c32a73b 100644 --- a/src/corelib/thread/qfutureinterface.cpp +++ b/src/corelib/thread/qfutureinterface.cpp @@ -292,7 +292,7 @@ void QFutureInterfaceBase::waitForResult(int resultIndex) // To avoid deadlocks and reduce the number of threads used, try to // run the runnable in the current thread. - d->pool()->d_func()->stealRunnable(d->runnable); + d->pool()->d_func()->stealAndRunRunnable(d->runnable); lock.relock(); @@ -313,7 +313,7 @@ void QFutureInterfaceBase::waitForFinished() lock.unlock(); if (!alreadyFinished) { - d->pool()->d_func()->stealRunnable(d->runnable); + d->pool()->d_func()->stealAndRunRunnable(d->runnable); lock.relock(); diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index 2a7cb8d66b..aaffdc2a4c 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -311,14 +311,12 @@ void QThreadPoolPrivate::clear() /*! \internal Searches for \a runnable in the queue, removes it from the queue and - runs it if found. This function does not return until the runnable - has completed. + returns \c true if it was found in the queue */ -void QThreadPoolPrivate::stealRunnable(QRunnable *runnable) +bool QThreadPoolPrivate::stealRunnable(QRunnable *runnable) { if (runnable == 0) - return; - bool found = false; + return false; { QMutexLocker locker(&mutex); QList<QPair<QRunnable *, int> >::iterator it = queue.begin(); @@ -326,17 +324,26 @@ void QThreadPoolPrivate::stealRunnable(QRunnable *runnable) while (it != end) { if (it->first == runnable) { - found = true; queue.erase(it); - break; + return true; } ++it; } } - if (!found) - return; + return false; +} + /*! + \internal + Searches for \a runnable in the queue, removes it from the queue and + runs it if found. This function does not return until the runnable + has completed. + */ +void QThreadPoolPrivate::stealAndRunRunnable(QRunnable *runnable) +{ + if (!stealRunnable(runnable)) + return; const bool autoDelete = runnable->autoDelete(); bool del = autoDelete && !--runnable->ref; @@ -628,6 +635,25 @@ void QThreadPool::clear() d->clear(); } +/*! + \since 5.5 + + Removes the specified \a runnable from the queue if it is not yet started. + The runnables for which \l{QRunnable::autoDelete()}{runnable->autoDelete()} + returns \c true are deleted. + + \sa start() +*/ +void QThreadPool::cancel(QRunnable *runnable) +{ + Q_D(QThreadPool); + if (!d->stealRunnable(runnable)) + return; + if (runnable->autoDelete() && !--runnable->ref) { + delete runnable; + } +} + QT_END_NAMESPACE #endif diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h index e4d23cc200..c8cc5dfd89 100644 --- a/src/corelib/thread/qthreadpool.h +++ b/src/corelib/thread/qthreadpool.h @@ -77,6 +77,7 @@ public: bool waitForDone(int msecs = -1); void clear(); + void cancel(QRunnable *runnable); }; QT_END_NAMESPACE diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h index d4308266a2..de2727adc1 100644 --- a/src/corelib/thread/qthreadpool_p.h +++ b/src/corelib/thread/qthreadpool_p.h @@ -76,7 +76,8 @@ public: void reset(); bool waitForDone(int msecs); void clear(); - void stealRunnable(QRunnable *); + bool stealRunnable(QRunnable *runnable); + void stealAndRunRunnable(QRunnable *runnable); mutable QMutex mutex; QSet<QThreadPoolThread *> allThreads; diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp index 9c2c8bf12a..1f1cf1db0c 100644 --- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp +++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp @@ -92,6 +92,7 @@ private slots: void priorityStart(); void waitForDone(); void clear(); + void cancel(); void waitForDoneTimeout(); void destroyingWaitsForTasksToFinish(); void stressTest(); @@ -958,6 +959,56 @@ void tst_QThreadPool::clear() QCOMPARE(count.load(), threadPool.maxThreadCount()); } +void tst_QThreadPool::cancel() +{ + QSemaphore sem(0); + class BlockingRunnable : public QRunnable + { + public: + QSemaphore & sem; + int & dtorCounter; + int & runCounter; + int dummy; + BlockingRunnable(QSemaphore & s, int & c, int & r) : sem(s), dtorCounter(c), runCounter(r){} + ~BlockingRunnable(){dtorCounter++;} + void run() + { + runCounter++; + sem.acquire(); + count.ref(); + } + }; + typedef BlockingRunnable* BlockingRunnablePtr; + + QThreadPool threadPool; + threadPool.setMaxThreadCount(3); + int runs = 2 * threadPool.maxThreadCount(); + BlockingRunnablePtr* runnables = new BlockingRunnablePtr[runs]; + count.store(0); + int dtorCounter = 0; + int runCounter = 0; + for (int i = 0; i < runs; i++) { + runnables[i] = new BlockingRunnable(sem, dtorCounter, runCounter); + runnables[i]->setAutoDelete(i != 0 && i != (runs-1)); //one which will run and one which will not + threadPool.cancel(runnables[i]); //verify NOOP for jobs not in the queue + threadPool.start(runnables[i]); + } + for (int i = 0; i < runs; i++) { + threadPool.cancel(runnables[i]); + } + runnables[0]->dummy = 0; //valgrind will catch this if cancel() is crazy enough to delete currently running jobs + runnables[runs-1]->dummy = 0; + QCOMPARE(dtorCounter, runs-threadPool.maxThreadCount()-1); + sem.release(threadPool.maxThreadCount()); + threadPool.waitForDone(); + QCOMPARE(runCounter, threadPool.maxThreadCount()); + QCOMPARE(count.load(), threadPool.maxThreadCount()); + QCOMPARE(dtorCounter, runs-2); + delete runnables[0]; //if the pool deletes them then we'll get double-free crash + delete runnables[runs-1]; + delete[] runnables; +} + void tst_QThreadPool::destroyingWaitsForTasksToFinish() { QTime total, pass; |