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)
|
QThreadPrivate::QThreadPrivate(QThreadData *d)
|
||||||
: QObjectPrivate(), running(false), finished(false),
|
: 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)
|
stackSize(0), priority(QThread::InheritPriority), data(d)
|
||||||
{
|
{
|
||||||
#if defined (Q_OS_UNIX)
|
#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
|
QT_END_NAMESPACE
|
||||||
|
@ -86,6 +86,9 @@ public:
|
|||||||
bool isFinished() const;
|
bool isFinished() const;
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
|
|
||||||
|
void requestInterruption();
|
||||||
|
bool isInterruptionRequested() const;
|
||||||
|
|
||||||
void setStackSize(uint stackSize);
|
void setStackSize(uint stackSize);
|
||||||
uint stackSize() const;
|
uint stackSize() const;
|
||||||
|
|
||||||
|
@ -150,6 +150,7 @@ public:
|
|||||||
bool running;
|
bool running;
|
||||||
bool finished;
|
bool finished;
|
||||||
bool isInFinish; //when in QThreadPrivate::finish
|
bool isInFinish; //when in QThreadPrivate::finish
|
||||||
|
bool interruptionRequested;
|
||||||
|
|
||||||
bool exited;
|
bool exited;
|
||||||
int returnCode;
|
int returnCode;
|
||||||
|
@ -377,6 +377,7 @@ void QThreadPrivate::finish(void *arg)
|
|||||||
d->thread_id = 0;
|
d->thread_id = 0;
|
||||||
d->running = false;
|
d->running = false;
|
||||||
d->finished = true;
|
d->finished = true;
|
||||||
|
d->interruptionRequested = false;
|
||||||
|
|
||||||
d->isInFinish = false;
|
d->isInFinish = false;
|
||||||
d->thread_done.wakeAll();
|
d->thread_done.wakeAll();
|
||||||
@ -549,6 +550,7 @@ void QThread::start(Priority priority)
|
|||||||
d->finished = false;
|
d->finished = false;
|
||||||
d->returnCode = 0;
|
d->returnCode = 0;
|
||||||
d->exited = false;
|
d->exited = false;
|
||||||
|
d->interruptionRequested = false;
|
||||||
|
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
|
@ -377,6 +377,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway)
|
|||||||
d->running = false;
|
d->running = false;
|
||||||
d->finished = true;
|
d->finished = true;
|
||||||
d->isInFinish = false;
|
d->isInFinish = false;
|
||||||
|
d->interruptionRequested = false;
|
||||||
|
|
||||||
if (!d->waiters) {
|
if (!d->waiters) {
|
||||||
CloseHandle(d->handle);
|
CloseHandle(d->handle);
|
||||||
@ -446,6 +447,7 @@ void QThread::start(Priority priority)
|
|||||||
d->finished = false;
|
d->finished = false;
|
||||||
d->exited = false;
|
d->exited = false;
|
||||||
d->returnCode = 0;
|
d->returnCode = 0;
|
||||||
|
d->interruptionRequested = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NOTE: we create the thread in the suspended state, set the
|
NOTE: we create the thread in the suspended state, set the
|
||||||
|
@ -106,6 +106,8 @@ private slots:
|
|||||||
|
|
||||||
void customEventDispatcher();
|
void customEventDispatcher();
|
||||||
|
|
||||||
|
void requestTermination();
|
||||||
|
|
||||||
#ifndef Q_OS_WINCE
|
#ifndef Q_OS_WINCE
|
||||||
void stressTest();
|
void stressTest();
|
||||||
#endif
|
#endif
|
||||||
@ -1345,5 +1347,43 @@ void tst_QThread::quitLock()
|
|||||||
QVERIFY(exitThreadCalled);
|
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)
|
QTEST_MAIN(tst_QThread)
|
||||||
#include "tst_qthread.moc"
|
#include "tst_qthread.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user