summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCorentin Jabot <corentinjabot@gmail.com>2013-01-20 13:24:30 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-05 08:20:18 +0200
commit5a02d30a78991240c3355d863f52b0d376ebf911 (patch)
treead02a7281b44f452016bae6a31ec3a25f8905596
parent34195aa6cbc3bbe2b0bb38a86a3fb3ad76c40499 (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.cpp60
-rw-r--r--src/corelib/thread/qthread.h3
-rw-r--r--src/corelib/thread/qthread_p.h1
-rw-r--r--src/corelib/thread/qthread_unix.cpp2
-rw-r--r--src/corelib/thread/qthread_win.cpp2
-rw-r--r--tests/auto/corelib/thread/qthread/tst_qthread.cpp40
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"