[Boost-bugs] [Boost C++ Libraries] #13008: [windows][Visual Studio compiler] Building Boost Thread with /GL causes leak in boost::thread_specific_ptr

Subject: [Boost-bugs] [Boost C++ Libraries] #13008: [windows][Visual Studio compiler] Building Boost Thread with /GL causes leak in boost::thread_specific_ptr
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2017-05-03 20:47:35


#13008: [windows][Visual Studio compiler] Building Boost Thread with /GL causes
leak in boost::thread_specific_ptr
-------------------------------+--------------------------
 Reporter: daniel.kruegler@… | Owner: anthonyw
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: thread
  Version: Boost 1.64.0 | Severity: Optimization
 Keywords: |
-------------------------------+--------------------------
 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 <iostream>
 #include "boost/config.hpp"
 #include "boost/thread/tss.hpp"
 #include "boost/atomic.hpp"

 #if !defined(BOOST_HAS_WINTHREADS)
 # error Windows platform required
 #endif

 #include <windows.h>

 // Uncomment to activate IO output for debugging purposes:
 #define BDAL_USE_IO_OUTPUT

 typedef int atomic_int_underlying_type;
 typedef boost::atomic<bool> atomic_bool_type;
 typedef boost::atomic<atomic_int_underlying_type> 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<InstanceCountingClass> 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.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/13008>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-05-03 20:52:40 UTC