diff options
author | Olivier Goffart <olivier.goffart@nokia.com> | 2011-03-25 18:15:39 (GMT) |
---|---|---|
committer | Olivier Goffart <olivier.goffart@nokia.com> | 2011-03-28 13:21:02 (GMT) |
commit | 0ee221b374ffef3657247be4c78e05689e04bef7 (patch) | |
tree | ed422aeaeca48be516e2a87867d0f7ca89c080a2 | |
parent | 383da4084ae0cd8dcb9777cbfa90a55c56f7ad09 (diff) | |
download | Qt-0ee221b374ffef3657247be4c78e05689e04bef7.zip Qt-0ee221b374ffef3657247be4c78e05689e04bef7.tar.gz Qt-0ee221b374ffef3657247be4c78e05689e04bef7.tar.bz2 |
Fix the leak of QAdoptedThread and all dependent objects
if there is a QEventDispatcher in QAdoptedThread.
QAdoptedThread is supposed to be destroyed when the QThreadData is
destroyed. But QThreadData is ref-counted, and QEventDispatcher holds
a reference to it. QEventDispatcher is supposed to be destroyed
in QThreadPrivate::finish, which is supposed to be called from the
destructor of QAdoptedThread.
There is a circular dependence. We break it by calling finish in the
callback that is called when the thread is finished.
Task-number: QTBUG-17986
Reviewed-by: Brad
-rw-r--r-- | src/corelib/thread/qthread.cpp | 6 | ||||
-rw-r--r-- | src/corelib/thread/qthread_p.h | 1 | ||||
-rw-r--r-- | src/corelib/thread/qthread_unix.cpp | 13 | ||||
-rw-r--r-- | src/corelib/thread/qthread_win.cpp | 13 | ||||
-rw-r--r-- | tests/auto/qthread/tst_qthread.cpp | 17 |
5 files changed, 44 insertions, 6 deletions
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 326f494..817e73e 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -78,7 +78,7 @@ QT_BEGIN_NAMESPACE QThreadData::QThreadData(int initialRefCount) : _ref(initialRefCount), thread(0), - quitNow(false), loopLevel(0), eventDispatcher(0), canWait(true) + quitNow(false), loopLevel(0), eventDispatcher(0), canWait(true), isAdopted(false) { // fprintf(stderr, "QThreadData %p created\n", this); } @@ -150,7 +150,7 @@ QAdoptedThread::QAdoptedThread(QThreadData *data) QAdoptedThread::~QAdoptedThread() { #ifndef QT_NO_THREAD - QThreadPrivate::finish(this); +// QThreadPrivate::finish(this); #endif // fprintf(stderr, "~QAdoptedThread = %p\n", this); } @@ -409,7 +409,7 @@ QThread::~QThread() wait(); locker.relock(); } - if (d->running && !d->finished) + if (d->running && !d->finished && !d->data->isAdopted) qWarning("QThread: Destroyed while thread is still running"); d->data->thread = 0; diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index 36e07c0..2413452 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -208,6 +208,7 @@ public: QPostEventList postEventList; bool canWait; QVector<void *> tls; + bool isAdopted; # ifdef Q_OS_SYMBIAN RThread symbian_thread_handle; diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 811a193..484b455 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -148,7 +148,16 @@ static void destroy_current_thread_data(void *p) // this destructor function, so we need to set it back to the // right value... pthread_setspecific(current_thread_data_key, p); - reinterpret_cast<QThreadData *>(p)->deref(); + QThreadData *data = static_cast<QThreadData *>(p); + if (data->isAdopted) { + QThread *thread = data->thread; + Q_ASSERT(thread); + QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread)); + Q_ASSERT(!thread_p->finished); + thread_p->finish(thread); + } + data->deref(); + // ... but we must reset it to zero before returning so we aren't // called again (POSIX allows implementations to call destructor // functions repeatedly until all values are zero) @@ -251,6 +260,7 @@ QThreadData *QThreadData::current() } data->deref(); } + data->isAdopted = true; if (!QCoreApplicationPrivate::theMainThread) QCoreApplicationPrivate::theMainThread = data->thread; } @@ -376,7 +386,6 @@ void QThreadPrivate::finish(void *arg) #else QMutexLocker locker(&d->mutex); #endif - d->isInFinish = true; d->priority = QThread::InheritPriority; bool terminated = d->terminated; diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp index 6b7932b..b9c55b0 100644 --- a/src/corelib/thread/qthread_win.cpp +++ b/src/corelib/thread/qthread_win.cpp @@ -124,6 +124,7 @@ QThreadData *QThreadData::current() } threadData->deref(); } + threadData->isAdopted = true; if (!QCoreApplicationPrivate::theMainThread) { QCoreApplicationPrivate::theMainThread = threadData->thread; @@ -231,7 +232,17 @@ void qt_adopted_thread_watcher_function(void *) } else { // printf("(qt) - qt_adopted_thread_watcher_function... called\n"); const int qthreadIndex = handleIndex - 1; - QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex))->deref(); + + QThreadData *data = QThreadData::get2(qt_adopted_qthreads.at(qthreadIndex)); + if (data->isAdopted) { + QThread *thread = data->thread; + Q_ASSERT(thread); + QThreadPrivate *thread_p = static_cast<QThreadPrivate *>(QObjectPrivate::get(thread)); + Q_ASSERT(!thread_p->finished); + thread_p->finish(thread); + } + data->deref(); + #if !defined(Q_OS_WINCE) || (defined(_WIN32_WCE) && (_WIN32_WCE>=0x600)) CloseHandle(qt_adopted_thread_handles.at(handleIndex)); #endif diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp index c69052e..12709db 100644 --- a/tests/auto/qthread/tst_qthread.cpp +++ b/tests/auto/qthread/tst_qthread.cpp @@ -103,6 +103,7 @@ private slots: void adoptedThreadExit(); void adoptedThreadExec(); void adoptedThreadFinished(); + void adoptedThreadExecFinished(); void adoptMultipleThreads(); void QTBUG13810_exitAndStart(); @@ -895,6 +896,21 @@ void tst_QThread::adoptedThreadFinished() QVERIFY(!QTestEventLoop::instance().timeout()); } +void tst_QThread::adoptedThreadExecFinished() +{ + NativeThreadWrapper nativeThread; + nativeThread.setWaitForStop(); + nativeThread.startAndWait(adoptedThreadExecFunction); + + QObject::connect(nativeThread.qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + nativeThread.stop(); + nativeThread.join(); + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + void tst_QThread::adoptMultipleThreads() { #if defined(Q_OS_WIN) @@ -929,6 +945,7 @@ void tst_QThread::adoptMultipleThreads() QTestEventLoop::instance().enterLoop(5); QVERIFY(!QTestEventLoop::instance().timeout()); QCOMPARE(int(recorder.activationCount), numThreads); + qDeleteAll(nativeThreads); } void tst_QThread::stressTest() |