From d85238e0cccc3494a1392e2c6438c8209a63c62d Mon Sep 17 00:00:00 2001 From: Vladimir Belyavsky Date: Wed, 1 Jun 2022 20:44:57 +0300 Subject: [PATCH] Windows: fix DeferredDelete events processing on QThread::terminate() On finishing/terminating a thread, when processing posted events, we need to consider QThread's own data instead of caller thread's data. Otherwise we can get into unexpected situations such as double destruction of an object, premature destruction, etc. Fixes: QTBUG-103922 Change-Id: Idf77221ebbaa0b150ee2d0c296b51829ae8dc30e Reviewed-by: Thiago Macieira (cherry picked from commit 865212057b9f4e2809ebe42ec25c2d94d818cde7) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/thread/qthread_win.cpp | 13 +++- .../corelib/thread/qthread/tst_qthread.cpp | 78 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index cda5fe4de13..5b53a275e51 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -329,6 +329,17 @@ unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(voi return 0; } +/* + For regularly terminating threads, this will be called and executed by the thread as the + last code before the thread exits. In that case, \a arg is the current QThread. + + However, this function will also be called by QThread::terminate (as well as wait() and + setTerminationEnabled) to give Qt a chance to update the terminated thread's state and + process pending DeleteLater events for objects that live in the terminated thread. And for + adopted thread, this method is called by the thread watcher. + + In those cases, \a arg will not be the current thread. +*/ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept { QThread *thr = reinterpret_cast(arg); @@ -340,7 +351,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept void **tls_data = reinterpret_cast(&d->data->tls); locker.unlock(); emit thr->finished(QThread::QPrivateSignal()); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplicationPrivate::sendPostedEvents(nullptr, QEvent::DeferredDelete, d->data); QThreadStorageData::finish(tls_data); locker.relock(); diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index b9c5eeb2432..ded43d77410 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -114,6 +114,9 @@ private slots: void create(); void createDestruction(); void threadIdReuse(); + + void terminateAndPrematureDestruction(); + void terminateAndDoubleDestruction(); }; enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; @@ -1746,5 +1749,80 @@ void tst_QThread::threadIdReuse() } } +class WaitToRun_Thread : public QThread +{ + Q_OBJECT +public: + void run() override + { + emit running(); + QThread::exec(); + } + +Q_SIGNALS: + void running(); +}; + + +void tst_QThread::terminateAndPrematureDestruction() +{ + WaitToRun_Thread thread; + QSignalSpy spy(&thread, &WaitToRun_Thread::running); + thread.start(); + QVERIFY(spy.wait(500)); + + QScopedPointer obj(new QObject); + QPointer pObj(obj.data()); + obj->deleteLater(); + + thread.terminate(); + QVERIFY2(pObj, "object was deleted prematurely!"); + thread.wait(500); +} + +void tst_QThread::terminateAndDoubleDestruction() +{ + class ChildObject : public QObject + { + public: + ChildObject(QObject *parent) + : QObject(parent) + { + QSignalSpy spy(&thread, &WaitToRun_Thread::running); + thread.start(); + spy.wait(500); + } + + ~ChildObject() + { + QVERIFY2(!inDestruction, "Double object destruction!"); + inDestruction = true; + thread.terminate(); + thread.wait(500); + } + + bool inDestruction = false; + WaitToRun_Thread thread; + }; + + class TestObject : public QObject + { + public: + TestObject() + : child(new ChildObject(this)) + { + } + + ~TestObject() + { + child->deleteLater(); + } + + ChildObject *child = nullptr; + }; + + TestObject obj; +} + QTEST_MAIN(tst_QThread) #include "tst_qthread.moc"