From 5e13c65b7083ba6a28820a82f2fa19ca8c1569b0 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 4 Nov 2010 17:23:04 +0100 Subject: Make QThreadStorage supports value type and not only pointers. Handling value type is much more natural than handling pointer. It was not possible to use normal type in QThreadStorage previously (probably because some compiler would not support it?) This should ease a lot the use of QThreadStorage and make it more intuitive. The only problem was the QThreadStorage::deleteData() that would not compile for nonn-pointer. This is now fixed Also updated the documentation. Reviewed-by: Joao Reviewed-by: Brad Task-number: QTBUG-15033 --- doc/src/snippets/threads/threads.cpp | 9 +-- src/corelib/thread/qthreadstorage.cpp | 47 +++++---------- src/corelib/thread/qthreadstorage.h | 13 ++++- tests/auto/qthreadstorage/tst_qthreadstorage.cpp | 74 ++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 39 deletions(-) diff --git a/doc/src/snippets/threads/threads.cpp b/doc/src/snippets/threads/threads.cpp index d8d1270..d16c398 100644 --- a/doc/src/snippets/threads/threads.cpp +++ b/doc/src/snippets/threads/threads.cpp @@ -93,15 +93,12 @@ private: typedef int SomeClass; //! [7] -QThreadStorage *> caches; +QThreadStorage > caches; void cacheObject(const QString &key, SomeClass *object) //! [7] //! [8] { - if (!caches.hasLocalData()) - caches.setLocalData(new QCache); - - caches.localData()->insert(key, object); + caches.localData().insert(key, object); } void removeFromCache(const QString &key) @@ -110,7 +107,7 @@ void removeFromCache(const QString &key) if (!caches.hasLocalData()) return; - caches.localData()->remove(key); + caches.localData().remove(key); } //! [9] diff --git a/src/corelib/thread/qthreadstorage.cpp b/src/corelib/thread/qthreadstorage.cpp index 2222427..8df6f39 100644 --- a/src/corelib/thread/qthreadstorage.cpp +++ b/src/corelib/thread/qthreadstorage.cpp @@ -216,19 +216,18 @@ void QThreadStorageData::finish(void **p) QThreadStorage is a template class that provides per-thread data storage. - \e{Note that due to compiler limitations, QThreadStorage can only - store pointers.} - The setLocalData() function stores a single thread-specific value for the calling thread. The data can be accessed later using - localData(). QThreadStorage takes ownership of the data (which - must be created on the heap with \c new) and deletes it when the - thread exits, either normally or via termination. + localData(). The hasLocalData() function allows the programmer to determine if data has previously been set using the setLocalData() function. This is also useful for lazy initializiation. + If T is a pointer type, QThreadStorage takes ownership of the data + (which must be created on the heap with \c new) and deletes it when + the thread exits, either normally or via termination. + For example, the following code uses QThreadStorage to store a single cache for each thread that calls the cacheObject() and removeFromCache() functions. The cache is automatically @@ -242,9 +241,6 @@ void QThreadStorageData::finish(void **p) \list - \o As noted above, QThreadStorage can only store pointers due to - compiler limitations. - \o The QThreadStorage destructor does not delete per-thread data. QThreadStorage only deletes per-thread data when the thread exits or when setLocalData() is called multiple times. @@ -280,8 +276,11 @@ void QThreadStorageData::finish(void **p) /*! \fn bool QThreadStorage::hasLocalData() const - Returns true if the calling thread has non-zero data available; - otherwise returns false. + If T is a pointer type, returns true if the calling thread has + non-zero data available. + + If T is a value type, returns wether the data has already been + constructed by calling setLocalData or localData. \sa localData() */ @@ -292,10 +291,8 @@ void QThreadStorageData::finish(void **p) Returns a reference to the data that was set by the calling thread. - Note: QThreadStorage can only store pointers. This function - returns a reference to the pointer that was set by the calling - thread. The value of this reference is 0 if no data was set by - the calling thread, + If no data has been set, this will create a default constructed + instance of type T. \sa hasLocalData() */ @@ -306,10 +303,6 @@ void QThreadStorageData::finish(void **p) Returns a copy of the data that was set by the calling thread. - Note: QThreadStorage can only store pointers. This function - returns a pointer to the data that was set by the calling thread. - If no data was set by the calling thread, this function returns 0. - \sa hasLocalData() */ @@ -319,19 +312,9 @@ void QThreadStorageData::finish(void **p) Sets the local data for the calling thread to \a data. It can be accessed later using the localData() functions. - If \a data is 0, this function deletes the previous data (if - any) and returns immediately. - - If \a data is non-zero, QThreadStorage takes ownership of the \a - data and deletes it automatically either when the thread exits - (either normally or via termination) or when setLocalData() is - called again. - - Note: QThreadStorage can only store pointers. The \a data - argument must be either a pointer to an object created on the heap - (i.e. using \c new) or 0. You should not delete \a data - yourself; QThreadStorage takes ownership and will delete the \a - data itself. + If T is a pointer type, QThreadStorage takes ownership of the data + and deletes it automatically either when the thread exits (either + normally or via termination) or when setLocalData() is called again. \sa localData(), hasLocalData() */ diff --git a/src/corelib/thread/qthreadstorage.h b/src/corelib/thread/qthreadstorage.h index 6264674..475d20d 100644 --- a/src/corelib/thread/qthreadstorage.h +++ b/src/corelib/thread/qthreadstorage.h @@ -91,6 +91,11 @@ inline void qThreadStorage_setLocalData(QThreadStorageData &d, T **t) { (void) d.set(*t); } +template +inline +void qThreadStorage_deleteData(void *d, T **) +{ delete static_cast(d); } + // value-based specialization template inline @@ -114,6 +119,12 @@ inline void qThreadStorage_setLocalData(QThreadStorageData &d, T *t) { (void) d.set(new T(*t)); } +template +inline +void qThreadStorage_deleteData(void *d, T *) +{ delete static_cast(d); } + + // MOC_SKIP_END #endif @@ -126,7 +137,7 @@ private: Q_DISABLE_COPY(QThreadStorage) static inline void deleteData(void *x) - { delete static_cast(x); } + { qThreadStorage_deleteData(x, reinterpret_cast(0)); } public: inline QThreadStorage() : d(deleteData) { } diff --git a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp index 54f8bd9..90e0311 100644 --- a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp +++ b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp @@ -78,6 +78,7 @@ private slots: void ensureCleanupOrder(); void QTBUG13877_crashOnExit(); void QTBUG14579_leakInDestructor(); + void valueBased(); }; class Pointer @@ -318,6 +319,7 @@ public: static QBasicAtomicInt count; inline SPointer() { count.ref(); } inline ~SPointer() { count.deref(); } + inline SPointer(const SPointer &other) { count.ref(); } }; QBasicAtomicInt SPointer::count = Q_BASIC_ATOMIC_INITIALIZER(0); @@ -382,5 +384,77 @@ void tst_QThreadStorage::QTBUG14579_leakInDestructor() QCOMPARE(int(SPointer::count), c); } +void tst_QThreadStorage::valueBased() +{ + struct Thread : QThread { + QThreadStorage &tlsSPointer; + QThreadStorage &tlsString; + QThreadStorage &tlsInt; + + int someNumber; + QString someString; + Thread(QThreadStorage &t1, QThreadStorage &t2, QThreadStorage &t3) + : tlsSPointer(t1), tlsString(t2), tlsInt(t3) { } + + void run() { + /*QVERIFY(!tlsSPointer.hasLocalData()); + QVERIFY(!tlsString.hasLocalData()); + QVERIFY(!tlsInt.hasLocalData());*/ + SPointer pointercopy = tlsSPointer.localData(); + + //Default constructed values + QVERIFY(tlsString.localData().isNull()); + QCOMPARE(tlsInt.localData(), 0); + + //setting + tlsString.setLocalData(someString); + tlsInt.setLocalData(someNumber); + + QCOMPARE(tlsString.localData(), someString); + QCOMPARE(tlsInt.localData(), someNumber); + + //changing + tlsSPointer.setLocalData(SPointer()); + tlsInt.localData() += 42; + tlsString.localData().append(QLatin1String(" world")); + + QCOMPARE(tlsString.localData(), (someString + QLatin1String(" world"))); + QCOMPARE(tlsInt.localData(), (someNumber + 42)); + + // operator= + tlsString.localData() = QString::number(someNumber); + QCOMPARE(tlsString.localData().toInt(), someNumber); + } + }; + + QThreadStorage tlsSPointer; + QThreadStorage tlsString; + QThreadStorage tlsInt; + + int c = SPointer::count; + + Thread t1(tlsSPointer, tlsString, tlsInt); + Thread t2(tlsSPointer, tlsString, tlsInt); + Thread t3(tlsSPointer, tlsString, tlsInt); + t1.someNumber = 42; + t2.someNumber = -128; + t3.someNumber = 78; + t1.someString = "hello"; + t2.someString = "trolltech"; + t3.someString = "nokia"; + + t1.start(); + t2.start(); + t3.start(); + + QVERIFY(t1.wait()); + QVERIFY(t2.wait()); + QVERIFY(t3.wait()); + + QCOMPARE(c, int(SPointer::count)); + +} + + QTEST_MAIN(tst_QThreadStorage) #include "tst_qthreadstorage.moc" -- cgit v0.12