QThread: replace three booleans with one state variable
There aren't 2^3 possible states for {running, isInFinish, finished}. There are only three possible states for them. There's a fourth case of NotStarted, which in my opinion is a design flaw. It needs to exist so QThread().isFinished() is false, but in a green field design we would be the same as QProcess: once a process/ thread finishes, it goes back to NotRunning. I also made the Windows version set back to NotStarted when failing to start, matching the Unix version. Drive-by use NSDMI. Change-Id: I262c3499666e4f4fbcfbfffd17cb3a48dad045dc Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
d05399c993
commit
452aaf340b
@ -127,8 +127,7 @@ QAdoptedThread::QAdoptedThread(QThreadData *data)
|
||||
// thread should be running and not finished for the lifetime
|
||||
// of the application (even if QCoreApplication goes away)
|
||||
#if QT_CONFIG(thread)
|
||||
d_func()->running = true;
|
||||
d_func()->finished = false;
|
||||
d_func()->threadState = QThreadPrivate::Running;
|
||||
init();
|
||||
d_func()->m_statusOrPendingObjects.setStatusAndClearList(
|
||||
QtPrivate::getBindingStatus({}));
|
||||
@ -170,10 +169,7 @@ QScopedScopeLevelCounter::~QScopedScopeLevelCounter()
|
||||
*/
|
||||
|
||||
QThreadPrivate::QThreadPrivate(QThreadData *d)
|
||||
: QObjectPrivate(), running(false), finished(false),
|
||||
isInFinish(false), interruptionRequested(false),
|
||||
exited(false), returnCode(-1),
|
||||
stackSize(0), priority(QThread::InheritPriority), data(d)
|
||||
: QObjectPrivate(), data(d)
|
||||
{
|
||||
|
||||
// INTEGRITY doesn't support self-extending stack. The default stack size for
|
||||
@ -492,12 +488,12 @@ QThread::~QThread()
|
||||
Q_D(QThread);
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
if (d->isInFinish) {
|
||||
if (d->threadState == QThreadPrivate::Finishing) {
|
||||
locker.unlock();
|
||||
wait();
|
||||
locker.relock();
|
||||
}
|
||||
if (d->running && !d->finished && !d->data->isAdopted)
|
||||
if (d->threadState == QThreadPrivate::Running && !d->data->isAdopted)
|
||||
qFatal("QThread: Destroyed while thread is still running");
|
||||
|
||||
d->data->thread.storeRelease(nullptr);
|
||||
@ -514,7 +510,7 @@ bool QThread::isFinished() const
|
||||
{
|
||||
Q_D(const QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
return d->finished || d->isInFinish;
|
||||
return d->threadState >= QThreadPrivate::Finishing;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -527,7 +523,7 @@ bool QThread::isRunning() const
|
||||
{
|
||||
Q_D(const QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
return d->running && !d->isInFinish;
|
||||
return d->threadState == QThreadPrivate::Running;
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -553,9 +549,9 @@ bool QThread::isRunning() const
|
||||
void QThread::setStackSize(uint stackSize)
|
||||
{
|
||||
Q_D(QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
Q_ASSERT_X(!d->running, "QThread::setStackSize",
|
||||
Q_ASSERT_X(!isRunning(), "QThread::setStackSize",
|
||||
"cannot change stack size while the thread is running");
|
||||
QMutexLocker locker(&d->mutex);
|
||||
d->stackSize = stackSize;
|
||||
}
|
||||
|
||||
@ -769,7 +765,7 @@ void QThread::setPriority(Priority priority)
|
||||
}
|
||||
Q_D(QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
if (!d->running) {
|
||||
if (d->threadState != QThreadPrivate::Running) {
|
||||
qWarning("QThread::setPriority: Cannot set priority, thread is not running");
|
||||
return;
|
||||
}
|
||||
@ -1223,7 +1219,7 @@ void QThread::requestInterruption()
|
||||
return;
|
||||
}
|
||||
QMutexLocker locker(&d->mutex);
|
||||
if (!d->running || d->finished || d->isInFinish)
|
||||
if (d->threadState != QThreadPrivate::Running)
|
||||
return;
|
||||
d->interruptionRequested.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
@ -1262,7 +1258,7 @@ bool QThread::isInterruptionRequested() const
|
||||
return false;
|
||||
// slow path: if the flag is set, take into account run status:
|
||||
QMutexLocker locker(&d->mutex);
|
||||
return d->running && !d->finished && !d->isInFinish;
|
||||
return d->threadState == QThreadPrivate::Running;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -184,16 +184,22 @@ public:
|
||||
mutable QMutex mutex;
|
||||
QAtomicInt quitLockRef;
|
||||
|
||||
bool running;
|
||||
bool finished;
|
||||
bool isInFinish; //when in QThreadPrivate::finish
|
||||
enum State : quint8 {
|
||||
// All state changes are imprecise
|
||||
NotStarted = 0, // before start() or if failed to start
|
||||
Running = 1, // in run()
|
||||
Finishing = 2, // in QThreadPrivate::finish()
|
||||
Finished = 3, // QThreadPrivate::finish() is done
|
||||
};
|
||||
|
||||
State threadState = NotStarted;
|
||||
bool exited = false;
|
||||
std::atomic<bool> interruptionRequested;
|
||||
|
||||
bool exited;
|
||||
int returnCode;
|
||||
int returnCode = -1;
|
||||
|
||||
uint stackSize;
|
||||
std::underlying_type_t<QThread::Priority> priority;
|
||||
uint stackSize = 0;
|
||||
std::underlying_type_t<QThread::Priority> priority = QThread::InheritPriority;
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
QWaitCondition thread_done;
|
||||
@ -226,7 +232,7 @@ public:
|
||||
|
||||
void deref()
|
||||
{
|
||||
if (!quitLockRef.deref() && running) {
|
||||
if (!quitLockRef.deref() && threadState == Running) {
|
||||
QCoreApplication::instance()->postEvent(q_ptr, new QEvent(QEvent::Quit));
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ static void destroy_current_thread_data(void *p)
|
||||
QThread *thread = data->thread.loadAcquire();
|
||||
Q_ASSERT(thread);
|
||||
QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
|
||||
Q_ASSERT(!thread_p->finished);
|
||||
Q_ASSERT(thread_p->threadState == QThreadPrivate::Running);
|
||||
thread_p->finish(thread);
|
||||
}
|
||||
data->deref();
|
||||
@ -347,7 +347,7 @@ void QThreadPrivate::finish(void *arg)
|
||||
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
d->isInFinish = true;
|
||||
d->threadState = QThreadPrivate::Finishing;
|
||||
d->priority = QThread::InheritPriority;
|
||||
void *data = &d->data->tls;
|
||||
locker.unlock();
|
||||
@ -366,11 +366,9 @@ void QThreadPrivate::finish(void *arg)
|
||||
locker.relock();
|
||||
}
|
||||
|
||||
d->running = false;
|
||||
d->finished = true;
|
||||
d->threadState = QThreadPrivate::Finished;
|
||||
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
||||
|
||||
d->isInFinish = false;
|
||||
d->data->threadId.storeRelaxed(nullptr);
|
||||
|
||||
d->thread_done.wakeAll();
|
||||
@ -629,14 +627,13 @@ void QThread::start(Priority priority)
|
||||
Q_D(QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
if (d->isInFinish)
|
||||
if (d->threadState == QThreadPrivate::Finishing)
|
||||
d->thread_done.wait(locker.mutex());
|
||||
|
||||
if (d->running)
|
||||
if (d->threadState == QThreadPrivate::Running)
|
||||
return;
|
||||
|
||||
d->running = true;
|
||||
d->finished = false;
|
||||
d->threadState = QThreadPrivate::Running;
|
||||
d->returnCode = 0;
|
||||
d->exited = false;
|
||||
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
||||
@ -702,8 +699,7 @@ void QThread::start(Priority priority)
|
||||
|
||||
// we failed to set the stacksize, and as the documentation states,
|
||||
// the thread will fail to run...
|
||||
d->running = false;
|
||||
d->finished = false;
|
||||
d->threadState = QThreadPrivate::NotStarted;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -736,8 +732,7 @@ void QThread::start(Priority priority)
|
||||
if (code) {
|
||||
qErrnoWarning(code, "QThread::start: Thread creation error");
|
||||
|
||||
d->running = false;
|
||||
d->finished = false;
|
||||
d->threadState = QThreadPrivate::NotStarted;
|
||||
d->data->threadId.storeRelaxed(nullptr);
|
||||
}
|
||||
}
|
||||
@ -768,10 +763,10 @@ bool QThread::wait(QDeadlineTimer deadline)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->finished || !d->running)
|
||||
if (d->threadState == QThreadPrivate::NotStarted)
|
||||
return true;
|
||||
|
||||
while (d->running) {
|
||||
while (d->threadState != QThreadPrivate::Finished) {
|
||||
if (!d->thread_done.wait(locker.mutex(), deadline))
|
||||
return false;
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ DWORD WINAPI qt_adopted_thread_watcher_function(LPVOID)
|
||||
Q_ASSERT(thread);
|
||||
auto thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread));
|
||||
Q_UNUSED(thread_p);
|
||||
Q_ASSERT(!thread_p->finished);
|
||||
Q_ASSERT(thread_p->threadState == QThreadPrivate::Running);
|
||||
QThreadPrivate::finish(thread);
|
||||
}
|
||||
data->deref();
|
||||
@ -290,7 +290,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
|
||||
QThreadPrivate *d = thr->d_func();
|
||||
|
||||
QMutexLocker locker(lockAnyway ? &d->mutex : nullptr);
|
||||
d->isInFinish = true;
|
||||
d->threadState = QThreadPrivate::Finishing;
|
||||
d->priority = QThread::InheritPriority;
|
||||
void **tls_data = reinterpret_cast<void **>(&d->data->tls);
|
||||
if (lockAnyway)
|
||||
@ -313,9 +313,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
|
||||
locker.relock();
|
||||
}
|
||||
|
||||
d->running = false;
|
||||
d->finished = true;
|
||||
d->isInFinish = false;
|
||||
d->threadState = QThreadPrivate::Finished;
|
||||
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
||||
|
||||
if (!d->waiters) {
|
||||
@ -377,20 +375,19 @@ void QThread::start(Priority priority)
|
||||
Q_D(QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
|
||||
if (d->isInFinish) {
|
||||
if (d->threadState == QThreadPrivate::Finishing) {
|
||||
locker.unlock();
|
||||
wait();
|
||||
locker.relock();
|
||||
}
|
||||
|
||||
if (d->running)
|
||||
if (d->threadState == QThreadPrivate::Running)
|
||||
return;
|
||||
|
||||
// avoid interacting with the binding system
|
||||
d->objectName = d->extraData ? d->extraData->objectName.valueBypassingBindings()
|
||||
: QString();
|
||||
d->running = true;
|
||||
d->finished = false;
|
||||
d->threadState = QThreadPrivate::Running;
|
||||
d->exited = false;
|
||||
d->returnCode = 0;
|
||||
d->interruptionRequested.store(false, std::memory_order_relaxed);
|
||||
@ -418,8 +415,7 @@ void QThread::start(Priority priority)
|
||||
|
||||
if (!d->handle) {
|
||||
qErrnoWarning("QThread::start: Failed to create thread");
|
||||
d->running = false;
|
||||
d->finished = true;
|
||||
d->threadState = QThreadPrivate::NotStarted;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -473,7 +469,7 @@ void QThread::terminate()
|
||||
{
|
||||
Q_D(QThread);
|
||||
QMutexLocker locker(&d->mutex);
|
||||
if (!d->running)
|
||||
if (d->threadState != QThreadPrivate::Running)
|
||||
return;
|
||||
if (!d->terminationEnabled) {
|
||||
d->terminatePending = true;
|
||||
@ -493,7 +489,7 @@ bool QThread::wait(QDeadlineTimer deadline)
|
||||
qWarning("QThread::wait: Thread tried to wait on itself");
|
||||
return false;
|
||||
}
|
||||
if (d->finished || !d->running)
|
||||
if (d->threadState == QThreadPrivate::NotStarted || d->threadState == QThreadPrivate::Finished)
|
||||
return true;
|
||||
|
||||
++d->waiters;
|
||||
@ -516,13 +512,13 @@ bool QThread::wait(QDeadlineTimer deadline)
|
||||
locker.mutex()->lock();
|
||||
--d->waiters;
|
||||
|
||||
if (ret && !d->finished) {
|
||||
if (ret && d->threadState < QThreadPrivate::Finished) {
|
||||
// thread was terminated by someone else
|
||||
|
||||
QThreadPrivate::finish(this, false);
|
||||
}
|
||||
|
||||
if (d->finished && !d->waiters) {
|
||||
if (d->threadState == QThreadPrivate::Finished && !d->waiters) {
|
||||
CloseHandle(d->handle);
|
||||
d->handle = 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user