[thread] thread_specific_ptr is not initialized with NULL
Hi, As I understand from documentation, once thread_specific_ptr is created, first call to get() should return NULL in all threads. Actually sometimes it doesn't happen. Consider the following scenario: 1) thread T1 initializes some global tss object (by doing "new" on global thread_specific_ptr<>*) 2) thread T2 tries to read the value with get(), and gets null. It sets a correct value (unsigned int 42) 3) thread T1 deletes tss object, and allocates a new one instead. 4) thread T2 tries to read the value from the new object So I expected that in (4) thread T2 will get NULL again. Actually, when new object created in (3) is created in same memory location, T2 gets the original value in (4), and not NULL. When new pointer allocated in (3) is different, T2 gets NULL as expected (verified using "placement new" to control location of the tss object). Is it a bug or inpropriate use pattern? The code that demonstrates the issue is listed below (please ignore synchronization issues) - note the line with a comment: when I do "new (buf2) ..." instead, it works as expected. Thanks, Michael ====================================== #include <boost/thread/tss.hpp> #include <boost/thread.hpp> #include <unistd.h> #include <iostream> using namespace std; boost::thread_specific_ptr<unsigned long> *pptr = NULL; char buf1[sizeof(boost::thread_specific_ptr<unsigned long>)]; char buf2[sizeof(boost::thread_specific_ptr<unsigned long>)]; void do1() { cout << "Creating TLS index\n"; pptr = new (buf1) boost::thread_specific_ptr<unsigned long>; sleep(5); cout << "Replacing TLD index\n"; pptr->~thread_specific_ptr(); pptr = new (buf1) boost::thread_specific_ptr<unsigned long>; // HERE WE CAN ALLOCATE IN buf1 OR buf2 } void printval() { unsigned long * val = pptr->get(); if (val == NULL) { cout << "Got NULL value\n"; val = new unsigned long(42); pptr->reset(val); } else { cout << "Got value: " << *val << endl; } } void do2() { for (int i = 0; i < 4; i++) { sleep(2); printval(); } } int main(int argc, char *argv[]) { cout << "size " << sizeof(boost::thread_specific_ptr<unsigned long>) << endl; boost::thread t1(do1); boost::thread t2(do2); t1.join(); t2.join(); return 0; } ============================ OUTPUT: Creating TLS index Got NULL value Got value: 42 Replacing TLD index Got value: 42 Got value: 42
"Michael Gopshtein" <mgopshtein@gmail.com> writes:
Hi,
As I understand from documentation, once thread_specific_ptr is created, first call to get() should return NULL in all threads. Actually sometimes it doesn't happen. Consider the following scenario:
1) thread T1 initializes some global tss object (by doing "new" on global thread_specific_ptr<>*) 2) thread T2 tries to read the value with get(), and gets null. It sets a correct value (unsigned int 42) 3) thread T1 deletes tss object, and allocates a new one instead.
This is now undefined behaviour. If you destroy a thread_specific_ptr whilst any thread has a non-NULL value then you cannot rely on what happens subsequently with respect to any thread that tries to use that or a new thread_specific_ptr instance.
4) thread T2 tries to read the value from the new object
So I expected that in (4) thread T2 will get NULL again. Actually, when new object created in (3) is created in same memory location, T2 gets the original value in (4), and not NULL. When new pointer allocated in (3) is different, T2 gets NULL as expected (verified using "placement new" to control location of the tss object).
Is it a bug or inpropriate use pattern?
Inappropriate use pattern. Anthony -- Author of C++ Concurrency in Action | http://www.manning.com/williams just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
Thanks. That's not the case with "native" TLS support (it promises to zero out all values in released slot), but it's good to know that this is undefined for thread_specific_ptr. BTW, is it because such pattern of use is not considered interesting? For example, in our application there are "Database" objects which are created and destroyed dynamically, and each Database has a unique connection per thread, which are stored in the TLS. The connections are also stored in seperate lists, and are released when the Database is no longer in use - so the "default" behavior of the native API (both Win32 and pthreads) is OK with us. Anyway, thanks again for the info. Michael "Anthony Williams" <anthony.ajw@gmail.com> wrote in message news:874ouru5pm.fsf@dell.justsoftwaresolutions.co.uk...
"Michael Gopshtein" <mgopshtein@gmail.com> writes:
This is now undefined behaviour. If you destroy a thread_specific_ptr whilst any thread has a non-NULL value then you cannot rely on what happens subsequently with respect to any thread that tries to use that or a new thread_specific_ptr instance.
Anthony -- Author of C++ Concurrency in Action | http://www.manning.com/williams just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
"Michael Gopshtein" <mgopshtein@gmail.com> writes:
That's not the case with "native" TLS support (it promises to zero out all values in released slot), but it's good to know that this is undefined for thread_specific_ptr.
BTW, is it because such pattern of use is not considered interesting? For example, in our application there are "Database" objects which are created and destroyed dynamically, and each Database has a unique connection per thread, which are stored in the TLS. The connections are also stored in seperate lists, and are released when the Database is no longer in use - so the "default" behavior of the native API (both Win32 and pthreads) is OK with us.
Allowing this is a potential for memory leaks. pthread_key_delete does not run the destructor function, so if you have dynamically allocated memory (or other resource) referenced by the key value you have a leak. By its very nature, thread_specific_ptr has dynamically allocated memory for each key value, so it is important to ensure that it is correctly deleted in each thread. Anthony -- Author of C++ Concurrency in Action | http://www.manning.com/williams just::thread C++0x thread library | http://www.stdthread.co.uk Just Software Solutions Ltd | http://www.justsoftwaresolutions.co.uk 15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
participants (2)
-
Anthony Williams
-
Michael Gopshtein