[thread] thread_specific_ptr and static initialization order

Hi, After upgrading from 1.42 to 1.43 (using VC10 x64), I encounter an access violation related to thread_specific_ptr. The program used to run fine with previous version of Boost on several platforms (Win x32, Win x64 and Linux x64). I've managed to create a small program that reproduces the problem. It seems to be a static initialization order related problem. If the TSS is initialized by a static object, the program fails (nullptr exception). However, if the static object is commented out, and the TSS is solely initialized in the main, the program runs fine. Is thread_specific_ptr supposed to work even when initialized by another static object? Or does it require to be initialized only after entering main? Regards, Tanguy #include <boost/thread/thread.hpp> #include <boost/thread/tss.hpp> #include <iostream> #include <stdexcept> namespace { boost::thread_specific_ptr<int> tss; void verifyTss() { if (tss.get() == nullptr) throw std::runtime_error("verifyTss failed (nullptr)"); } class InitTss { public: InitTss() { tss.reset(new int); } }; InitTss init_tss; } int main(int argc, char argv[]) { try { tss.reset(new int); verifyTss(); boost::thread thread([] () { tss.reset(new int); }); thread.join(); verifyTss(); } catch (const std::exception & error) { std::cerr << "ERROR: " << error.what() << std::endl; } }

On Sat, May 8, 2010 at 7:56 PM, Tanguy Fautré <tfautre@telenet.be> wrote:
Hi,
After upgrading from 1.42 to 1.43 (using VC10 x64), I encounter an access violation related to thread_specific_ptr. The program used to run fine with previous version of Boost on several platforms (Win x32, Win x64 and Linux x64).
I've managed to create a small program that reproduces the problem. It seems to be a static initialization order related problem. If the TSS is initialized by a static object, the program fails (nullptr exception). However, if the static object is commented out, and the TSS is solely initialized in the main, the program runs fine.
Is thread_specific_ptr supposed to work even when initialized by another static object? Or does it require to be initialized only after entering main?
I don't believe there are any inter-compilation unit dependencies hence it should work if your initialization order is compliant with the usual C++ standard guarantees.
Regards,
Tanguy
The supplied C++ example does not reproduce the problem for me with the boost trunk or the boost release branch in debug or release configurations using the default compilation options on VC 10 Express. I have checked for changes between the release branch and the 1.43 release and nothing relevant appears to have changed. Would you please provide more details about how to reproduce the problem? Regards, Neil Groves

On 09/05/2010 21:53, Neil Groves wrote:
On Sat, May 8, 2010 at 7:56 PM, Tanguy Fautré<tfautre@telenet.be> wrote:
Is thread_specific_ptr supposed to work even when initialized by another static object? Or does it require to be initialized only after entering main?
I don't believe there are any inter-compilation unit dependencies hence it should work if your initialization order is compliant with the usual C++ standard guarantees.
That was my understanding too.
The supplied C++ example does not reproduce the problem for me with the boost trunk or the boost release branch in debug or release configurations using the default compilation options on VC 10 Express. I have checked for changes between the release branch and the 1.43 release and nothing relevant appears to have changed.
Would you please provide more details about how to reproduce the problem?
Find all the details below. Note that the problem only appears in debug mode. It seems that in the last call to verifyTss(), TlsGetValue() (boost_1_43_0\libs\thread\src\win32\thread.cpp, line 53) returns NULL (with GetLastError() == 0) instead of the expected thread data. I've got everything compiled in x64 on Win7. I'll give it a try tomorrow at the office with Boost in x86. Regards, Tanguy 1/ Compiled Boost 1.43.0 by running the following bat script in "Visual Studio x64 Win64 Command Prompt (2010)" (the ZLIB defines are probably irrelevant in this case, but I copy/pasted the whole thing just in case). bjam.exe -j2 --toolset=msvc --build-type=complete address-model=64 define=_CRT_SECURE_NO_WARNINGS=0 define=_SCL_SECURE_NO_WARNINGS=0 define=ZLIB_INCLUDE=C:\Development\libraries\platform\win64\vc100\include define=ZLIB_LIBPATH=C:\Development\libraries\platform\win64\vc100\lib runtime-link=shared link=shared debug install bjam.exe -j2 --toolset=msvc --build-type=complete address-model=64 define=_CRT_SECURE_NO_WARNINGS=0 define=_SCL_SECURE_NO_WARNINGS=0 define=ZLIB_INCLUDE=C:\Development\libraries\platform\win64\vc100\include define=ZLIB_LIBPATH=C:\Development\libraries\platform\win64\vc100\lib runtime-link=shared link=static debug install bjam.exe -j2 --toolset=msvc --build-type=complete address-model=64 define=_CRT_SECURE_NO_WARNINGS=0 define=_SCL_SECURE_NO_WARNINGS=0 define=ZLIB_INCLUDE=C:\Development\libraries\platform\win64\vc100\include define=ZLIB_LIBPATH=C:\Development\libraries\platform\win64\vc100\lib runtime-link=shared link=shared release install bjam.exe -j2 --toolset=msvc --build-type=complete address-model=64 define=_CRT_SECURE_NO_WARNINGS=0 define=_SCL_SECURE_NO_WARNINGS=0 define=ZLIB_INCLUDE=C:\Development\libraries\platform\win64\vc100\include define=ZLIB_LIBPATH=C:\Development\libraries\platform\win64\vc100\lib runtime-link=shared link=static release install 2/ Compiled the given example program using CMake 2.8.1 with VC10 x64. CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(TssTest) IF (WIN32) ADD_DEFINITIONS(-DUNICODE -D_UNICODE) ADD_DEFINITIONS(-DWIN32_LEAN_AND_MEAN) ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) SET(Boost_USE_STATIC_LIBS ON) ENDIF (WIN32) FIND_PACKAGE(Boost REQUIRED COMPONENTS thread) INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) ADD_EXECUTABLE(main tss_static_order.cpp) TARGET_LINK_LIBRARIES(main ${Boost_THREAD_LIBRARY}) 3/ Ran the program in Debug mode. It fails by printing "ERROR: verifyTss failed (nullptr)".

On 09/05/2010 23:36, Tanguy Fautré wrote:
Note that the problem only appears in debug mode. It seems that in the last call to verifyTss(), TlsGetValue() (boost_1_43_0\libs\thread\src\win32\thread.cpp, line 53) returns NULL (with GetLastError() == 0) instead of the expected thread data.
Found the problem! It is indeed a static initialization order. The problem is that create_current_thread_tls_key() is called twice, even though it should be protected by call_once (line 58), leading to two TLS instead of one. boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key); is being called (from init_tss static constructor) before "current_thread_tls_init_flag" is properly initialized with BOOST_ONCE_INIT (line 28). Note that boost::once_flag is a struct type with a destructor and no constructor. BOOST_ONCE_INIT is a define to {0,0,0,0}. Effectively leading to: boost::once_flag current_thread_tls_init_flag={0,0,0,0}; I've got no idea what the standard says about such a case with respect to the static initialization order problem. Tanguy

On 10/05/2010 00:13, Tanguy Fautré wrote:
boost::once_flag current_thread_tls_init_flag={0,0,0,0};
I've got no idea what the standard says about such a case with respect to the static initialization order problem.
It seems that a recent change (commit 57889) broke the POD guarantees of boost::once_flag by adding a destructor.
participants (2)
-
Neil Groves
-
Tanguy Fautré