id summary reporter owner description type status milestone component version severity resolution keywords cc 13008 [windows][Visual Studio compiler] Building Boost Thread with /GL causes leak in boost::thread_specific_ptr daniel.kruegler@… Anthony Williams "When Boost Thread is build using the Visual Studio compiler with the optimization flag /GL (""Whole Program Optimization"") added, and when a program links statically against Boost Thread for release builds, instances of `boost::thread_specific_ptr` don't call the cleanup function for object instances in other threads when threads have been started via windows API (not using boost::thread). The problem occurs for both 32-bit and 64-bit systems, and has been observed on Windows 7 and Windows 10, and for both auto-linking and not auto-linking situations. How to reproduce: a) Build the relevant Boost libraries using the following command: {{{ b2 --build-dir=""%TMP%"" toolset=msvc-14.1 --with-thread --with-system --with-date_time --with-atomic address-model=32 link=static,shared variant=release asynch-exceptions=on extern-c-nothrow=off rtti=on optimization=speed cxxflags=""/GL"" linkflags=""/LTCG:incremental"" --stagedir=C:\SomePath --compiler=""C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.10.25017\bin\HostX64\x64\cl.exe"" }}} b) Build statically against the following translation unit in release mode and run the program: {{{ #include #include ""boost/config.hpp"" #include ""boost/thread/tss.hpp"" #include ""boost/atomic.hpp"" #if !defined(BOOST_HAS_WINTHREADS) # error Windows platform required #endif #include // Uncomment to activate IO output for debugging purposes: #define BDAL_USE_IO_OUTPUT typedef int atomic_int_underlying_type; typedef boost::atomic atomic_bool_type; typedef boost::atomic atomic_integral; atomic_integral instance_counter(0); atomic_bool_type test_result(false); struct InstanceCountingClass { InstanceCountingClass() { ++instance_counter; #ifdef BDAL_USE_IO_OUTPUT std::cout << ""[InstanceCountingClass] default c'tor"" << std::endl; #endif } InstanceCountingClass(const InstanceCountingClass&) { ++instance_counter; #ifdef BDAL_USE_IO_OUTPUT std::cout << ""[InstanceCountingClass] copy c'tor"" << std::endl; #endif } ~InstanceCountingClass() { --instance_counter; #ifdef BDAL_USE_IO_OUTPUT std::cout << ""[InstanceCountingClass] d'tor"" << std::endl; #endif } }; boost::thread_specific_ptr tss; DWORD WINAPI myThreadFunction(LPVOID) { #ifdef BDAL_USE_IO_OUTPUT std::cout << ""[myThreadFunction] called"" << std::endl; #endif atomic_int_underlying_type cnt = instance_counter.load(); if (cnt != 1) { std::cerr << ""[myThreadFunction] TSS instance counter is different from 1: "" << cnt << std::endl; test_result.store(false); } if (tss.get()) { std::cerr << ""[myThreadFunction] TSS contains unexpected value"" << std::endl; test_result.store(false); } tss.reset(new InstanceCountingClass()); cnt = instance_counter.load(); if (cnt != 2) { std::cerr << ""[myThreadFunction] TSS instance counter is different from 2: "" << cnt << std::endl; test_result.store(false); } return 0; } bool doTestBoostThreadSpecificPtrCleanup() { instance_counter.store(0); test_result.store(true); if (tss.get()) { std::cerr << ""[doTestBoostThreadSpecificPtrCleanup] TSS contains unexpected value"" << std::endl; return false; } tss.reset(new InstanceCountingClass()); // Use Windows API to instantiate a thread HANDLE threadhandle = ::CreateThread(NULL, 0, myThreadFunction, NULL, 0, 0); if (!threadhandle) { std::cerr << ""[doTestBoostThreadSpecificPtrCleanup] CreateThread failed"" << std::endl; return false; } // Wait for thread to exit DWORD rc = ::WaitForSingleObject(threadhandle, INFINITE); if (!::CloseHandle(threadhandle) || rc != WAIT_OBJECT_0 || !test_result) { std::cerr << ""[doTestBoostThreadSpecificPtrCleanup] WaitForSingleObject or CloseHandle failed"" << std::endl; return false; } // Make sure that the thread-specific instance has been destroyed, // even though we haven't used the Boost.Thread API to start the // thread. atomic_int_underlying_type cnt = instance_counter.load(); if (cnt != 1) { std::cerr << ""[doTestBoostThreadSpecificPtrCleanup] TSS instance counter is different from 1: "" << cnt << std::endl; return false; } return true; } int main() { if (doTestBoostThreadSpecificPtrCleanup()) { std::cout << ""SUCCESS!"" << std::endl; } else { std::cerr << ""FAILURE!"" << std::endl; } } }}} Observed output: {{{ [InstanceCountingClass] default c'tor [myThreadFunction] called [InstanceCountingClass] default c'tor [doTestBoostThreadSpecificPtrCleanup] TSS instance counter is different from 1: 2 FAILURE! [InstanceCountingClass] d'tor }}} Verified for: Boost versions: 1.64, 1.63[[br]] Visual Studio versions: VS 2012 (toolset=msvc-11.0), VS 2015 (toolset=msvc-14.0), VS 2017 (toolset=msvc-14.1) Workaround: Don't build Boost using the /GL compiler flag." Bugs new To Be Determined thread Boost 1.64.0 Optimization