diff options
-rw-r--r-- | src/corelib/thread/qthread_win.cpp | 13 | ||||
-rw-r--r-- | tests/auto/corelib/thread/qthread/tst_qthread.cpp | 78 |
2 files changed, 90 insertions, 1 deletions
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index dc069a03c7..384d80dcb6 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -293,6 +293,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<QThread *>(arg); @@ -305,7 +316,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept if (lockAnyway) locker.unlock(); emit thr->finished(QThread::QPrivateSignal()); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QCoreApplicationPrivate::sendPostedEvents(nullptr, QEvent::DeferredDelete, d->data); QThreadStorageData::finish(tls_data); if (lockAnyway) locker.relock(); diff --git a/tests/auto/corelib/thread/qthread/tst_qthread.cpp b/tests/auto/corelib/thread/qthread/tst_qthread.cpp index 125c969ea2..b5f2ebdabf 100644 --- a/tests/auto/corelib/thread/qthread/tst_qthread.cpp +++ b/tests/auto/corelib/thread/qthread/tst_qthread.cpp @@ -91,6 +91,9 @@ private slots: void create(); void createDestruction(); void threadIdReuse(); + + void terminateAndPrematureDestruction(); + void terminateAndDoubleDestruction(); }; enum { one_minute = 60 * 1000, five_minutes = 5 * one_minute }; @@ -1719,5 +1722,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<QObject> obj(new QObject); + QPointer<QObject> 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" |