diff options
author | Olivier Goffart <olivier.goffart@nokia.com> | 2010-11-04 12:37:48 (GMT) |
---|---|---|
committer | Olivier Goffart <olivier.goffart@nokia.com> | 2010-11-22 14:09:06 (GMT) |
commit | ed8f3b6c98f1b305f0d183bc70c5f810a9c45ef2 (patch) | |
tree | 22aef970e281ab956b34d1f25ca6f7d037f57caa | |
parent | fb026f81bfe64be232a819fdac5b8dbcdd4fae4d (diff) | |
download | Qt-ed8f3b6c98f1b305f0d183bc70c5f810a9c45ef2.zip Qt-ed8f3b6c98f1b305f0d183bc70c5f810a9c45ef2.tar.gz Qt-ed8f3b6c98f1b305f0d183bc70c5f810a9c45ef2.tar.bz2 |
QThreadStorage: fix memory leak if thread storage are added while destroying
The destructor(q) function could use itself and create new QThreadStorage.
This might be the case for example, when using qDebug in a destructor.
Task-number: QTBUG-14579
Reveiwed-by: Joao
Reviewed-by: Brad
-rw-r--r-- | src/corelib/thread/qthreadstorage.cpp | 7 | ||||
-rw-r--r-- | tests/auto/qthreadstorage/tst_qthreadstorage.cpp | 72 |
2 files changed, 76 insertions, 3 deletions
diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 2fc04f5..2222427 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -178,11 +178,12 @@ void QThreadStorageData::finish(void **p) return; // nothing to do DEBUG_MSG("QThreadStorageData: Destroying storage for thread %p", QThread::currentThread()); - - for(int i = tls->size() - 1; i >= 0; i--) { - void *&value = (*tls)[i]; + while (!tls->isEmpty()) { + void *&value = tls->last(); void *q = value; value = 0; + int i = tls->size() - 1; + tls->resize(i); if (!q) { // data already deleted diff --git a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp index ed86165..54f8bd9 100644 --- a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp +++ b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp @@ -77,6 +77,7 @@ private slots: void adoptedThreads(); void ensureCleanupOrder(); void QTBUG13877_crashOnExit(); + void QTBUG14579_leakInDestructor(); }; class Pointer @@ -310,5 +311,76 @@ void tst_QThreadStorage::QTBUG13877_crashOnExit() QVERIFY(process.exitStatus() != QProcess::CrashExit); } +// S stands for thread Safe. +class SPointer +{ +public: + static QBasicAtomicInt count; + inline SPointer() { count.ref(); } + inline ~SPointer() { count.deref(); } +}; +QBasicAtomicInt SPointer::count = Q_BASIC_ATOMIC_INITIALIZER(0); + +Q_GLOBAL_STATIC(QThreadStorage<SPointer *>, QTBUG14579_pointers1) +Q_GLOBAL_STATIC(QThreadStorage<SPointer *>, QTBUG14579_pointers2) + +class QTBUG14579_class +{ +public: + SPointer member; + inline ~QTBUG14579_class() { + QVERIFY(!QTBUG14579_pointers1()->hasLocalData()); + QVERIFY(!QTBUG14579_pointers2()->hasLocalData()); + QTBUG14579_pointers2()->setLocalData(new SPointer); + QTBUG14579_pointers1()->setLocalData(new SPointer); + QVERIFY(QTBUG14579_pointers1()->hasLocalData()); + QVERIFY(QTBUG14579_pointers2()->hasLocalData()); + } +}; + + +void tst_QThreadStorage::QTBUG14579_leakInDestructor() +{ + class Thread : public QThread + { + public: + QThreadStorage<QTBUG14579_class *> &tls; + + Thread(QThreadStorage<QTBUG14579_class *> &t) : tls(t) { } + + void run() + { + QVERIFY(!tls.hasLocalData()); + tls.setLocalData(new QTBUG14579_class); + QVERIFY(tls.hasLocalData()); + } + }; + int c = SPointer::count; + + QThreadStorage<QTBUG14579_class *> tls; + + QVERIFY(!QTBUG14579_pointers1()->hasLocalData()); + QThreadStorage<int *> tls2; //add some more tls to make sure ids are not following each other too much + QThreadStorage<int *> tls3; + QVERIFY(!tls2.hasLocalData()); + QVERIFY(!tls3.hasLocalData()); + QVERIFY(!tls.hasLocalData()); + + Thread t1(tls); + Thread t2(tls); + Thread t3(tls); + + t1.start(); + t2.start(); + t3.start(); + + QVERIFY(t1.wait()); + QVERIFY(t2.wait()); + QVERIFY(t3.wait()); + + //check all the constructed things have been destructed + QCOMPARE(int(SPointer::count), c); +} + QTEST_MAIN(tst_QThreadStorage) #include "tst_qthreadstorage.moc" |