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