diff options
author | Corentin Jabot <corentinjabot@gmail.com> | 2013-01-20 13:24:30 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2013-09-05 08:20:18 +0200 |
commit | 5a02d30a78991240c3355d863f52b0d376ebf911 (patch) | |
tree | ad02a7281b44f452016bae6a31ec3a25f8905596 | |
parent | 34195aa6cbc3bbe2b0bb38a86a3fb3ad76c40499 (diff) |
Add an advisory interruption mechanism to QThread.
To ease interruption of long running tasks, a new method
QThread::setInterruptionRequested() can be called.
The task can check QThread::isInterruptionRequested()
and act upon it by stopping itself.
These methods are designed to replace the use of a global variable
and other hacky ways to stop a task running in another thread.
Change-Id: I17622dd60d2262078210e7e4294ad6c53a6dc179
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
-rw-r--r-- | src/corelib/thread/qthread.cpp | 60 | ||||
-rw-r--r-- | src/corelib/thread/qthread.h | 3 | ||||
-rw-r--r-- | src/corelib/thread/qthread_p.h | 1 | ||||
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qthread_win.cpp | 2 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qthread/tst_qthread.cpp | 40 |
6 files changed, 107 insertions, 1 deletions
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 4d5bee3154..35d57b3d83 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -146,7 +146,8 @@ void QAdoptedThread::run() QThreadPrivate::QThreadPrivate(QThreadData *d) : QObjectPrivate(), running(false), finished(false), - isInFinish(false), exited(false), returnCode(-1), + isInFinish(false), interruptionRequested(false), + exited(false), returnCode(-1), stackSize(0), priority(QThread::InheritPriority), data(d) { #if defined (Q_OS_UNIX) @@ -801,4 +802,61 @@ bool QThread::event(QEvent *event) } } +/*! + \since 5.2 + + Request the interruption of the thread. + That request is advisory and it is up to code running on the thread to decide + if and how it should act upon such request. + This function does not stop any event loop running on the thread and + does not terminate it in any way. + + \sa isInterruptionRequested() +*/ + +void QThread::requestInterruption() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + if (!d->running || d->finished || d->isInFinish) + return; + if (this == QCoreApplicationPrivate::theMainThread) { + qWarning("QThread::requestInterruption has no effect on the main thread"); + return; + } + d->interruptionRequested = true; +} + +/*! + \since 5.2 + + Return true if the task running on this thread should be stopped. + An interruption can be requested by requestInterruption(). + + This function can be used to make long running tasks cleanly interruptible. + Never checking or acting on the value returned by this function is safe, + however it is advisable do so regularly in long running functions. + Take care not to call it too often, to keep the overhead low. + + \code + void long_task() { + forever { + if ( QThread::currentThread()->isInterruptionRequested() ) { + return; + } + } + } + \endcode + + \sa currentThread() requestInterruption() +*/ +bool QThread::isInterruptionRequested() const +{ + Q_D(const QThread); + QMutexLocker locker(&d->mutex); + if (!d->running || d->finished || d->isInFinish) + return false; + return d->interruptionRequested; +} + QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 19c0f82d86..f06981c0bd 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -86,6 +86,9 @@ public: bool isFinished() const; bool isRunning() const; + void requestInterruption(); + bool isInterruptionRequested() const; + void setStackSize(uint stackSize); uint stackSize() const; diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 9d773b3c1c..5e4eedaac7 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -150,6 +150,7 @@ public: bool running; bool finished; bool isInFinish; //when in QThreadPrivate::finish + bool interruptionRequested; bool exited; int returnCode; diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 6a91193785..15558cb148 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -377,6 +377,7 @@ void QThreadPrivate::finish(void *arg) d->thread_id = 0; d->running = false; d->finished = true; + d->interruptionRequested = false; d->isInFinish = false; d->thread_done.wakeAll(); @@ -549,6 +550,7 @@ void QThread::start(Priority priority) d->finished = false; d->returnCode = 0; d->exited = false; + d->interruptionRequested = false; pthread_attr_t attr; pthread_attr_init(&attr); diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index a0fac8eff2..d49a6a9a8e 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -377,6 +377,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) d->running = false; d->finished = true; d->isInFinish = false; + d->interruptionRequested = false; if (!d->waiters) { CloseHandle(d->handle); @@ -446,6 +447,7 @@ void QThread::start(Priority priority) d->finished = false; d->exited = false; d->returnCode = 0; + d->interruptionRequested = false; /* NOTE: we create the thread in the suspended state, set the diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 1ee628dde5..5cc0e5bdb4 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -106,6 +106,8 @@ private slots: void customEventDispatcher(); + void requestTermination(); + #ifndef Q_OS_WINCE void stressTest(); #endif @@ -1345,5 +1347,43 @@ void tst_QThread::quitLock() QVERIFY(exitThreadCalled); } +class StopableJob : public QObject +{ + Q_OBJECT +public: + StopableJob (QSemaphore &sem) : sem(sem) {} + QSemaphore &sem; +public Q_SLOTS: + void run() { + sem.release(); + while (!thread()->isInterruptionRequested()) + QTest::qSleep(10); + sem.release(); + Q_EMIT finished(); + } +Q_SIGNALS: + void finished(); +}; + +void tst_QThread::requestTermination() +{ + QThread thread; + QVERIFY(!thread.isInterruptionRequested()); + QSemaphore sem; + StopableJob *j = new StopableJob(sem); + j->moveToThread(&thread); + connect(&thread, &QThread::started, j, &StopableJob::run); + connect(j, &StopableJob::finished, &thread, &QThread::quit, Qt::DirectConnection); + connect(&thread, &QThread::finished, j, &QObject::deleteLater); + thread.start(); + QVERIFY(!thread.isInterruptionRequested()); + sem.acquire(); + QVERIFY(!thread.wait(1000)); + thread.requestInterruption(); + sem.acquire(); + QVERIFY(thread.wait(1000)); + QVERIFY(!thread.isInterruptionRequested()); +} + QTEST_MAIN(tst_QThread) #include "tst_qthread.moc" |