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>
This commit is contained in:
parent
34195aa6cb
commit
5a02d30a78
@ -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
|
||||
|
@ -86,6 +86,9 @@ public:
|
||||
bool isFinished() const;
|
||||
bool isRunning() const;
|
||||
|
||||
void requestInterruption();
|
||||
bool isInterruptionRequested() const;
|
||||
|
||||
void setStackSize(uint stackSize);
|
||||
uint stackSize() const;
|
||||
|
||||
|
@ -150,6 +150,7 @@ public:
|
||||
bool running;
|
||||
bool finished;
|
||||
bool isInFinish; //when in QThreadPrivate::finish
|
||||
bool interruptionRequested;
|
||||
|
||||
bool exited;
|
||||
int returnCode;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user