Opened 11 years ago
Last modified 11 years ago
#5673 new Bugs
Quick allocator error in multi thread
Reported by: | Owned by: | Peter Dimov | |
---|---|---|---|
Milestone: | To Be Determined | Component: | smart_ptr |
Version: | Boost 1.44.0 | Severity: | Problem |
Keywords: | Cc: |
Description
Compiler: MSVS 2008 use macros BOOST_SP_USE_QUICK_ALLOCATOR
Exception (Access vialation by reading address) occured by constructor of shared_ptr.
Cause of exception is multi thread creation of static mutex in quick allocator.
shared_ptr call quick allocator. quick allocator use next code:
lightweight_mutex::scoped_lock lock( mutex() );
function mutex() return internal static variable:
boost_1_44_0\boost\smart_ptr\detail\quick_allocator.hpp template<unsigned size, unsigned align_> struct allocator_impl { ... static lightweight_mutex & mutex() { static freeblock< sizeof( lightweight_mutex ), boost::alignment_of< lightweight_mutex >::value > fbm; static lightweight_mutex * pm = new( &fbm ) lightweight_mutex; return *pm; } ... }
2 thread call mutex() (first time for this instantiation of template class allocator_impl). One start init pm and go to sleep. Other check that pm is initialized and return its value (in my case NULL) and raise exception.
Initialization of static variable in C++ is not safe for multi thread. This code is incorrect. In dependence from realization of static variable you will be have access vialation or memory leak - flag of static variable initialization (construction) set before or after initialization.
In MSVS 2008 flag set before and you take access vialation.
Decision
Not resolve, but minimize problem.
Idea:
As I understand, This problem can appear in first initialization of every instantiation of shared_ptr - first use every instantiation of allocator_impl.
If you synchronize of initialization pm then problem can appear only first initialization of any instantiation of shared_ptr. And I can create first fictive shared_ptr in non-multi thread and then normal use shared_ptr in multi thread.
Realization:
boost_1_44_0\boost\smart_ptr\detail\quick_allocator.hpp lightweight_mutex & global_mutex() { static freeblock< sizeof( lightweight_mutex ), boost::alignment_of< lightweight_mutex >::value > fbm; static lightweight_mutex * pm = new( &fbm ) lightweight_mutex; return *pm; } template<unsigned size, unsigned align_> struct allocator_impl { static lightweight_mutex & mutex() { static freeblock< sizeof( lightweight_mutex ), boost::alignment_of< lightweight_mutex >::value > fbm; static lightweight_mutex * pm = NULL; if(pm == NULL) { lightweight_mutex::scoped_lock lock( global_mutex() ); if(pm == NULL) { pm = new( &fbm ) lightweight_mutex; } } return *pm; } }
Change History (3)
comment:1 by , 11 years ago
Component: | None → smart_ptr |
---|---|
Owner: | set to |
comment:2 by , 11 years ago
comment:3 by , 11 years ago
With global_mutex I can protect me from crash.
At start (then I have only one thread) I can create first fictive shared_ptr. It initialize global_mutex. And next usage of any shared_ptr will be synchronize.
Now I not sure in usage of shared_ptr because first usage new instantiation could use new mutex and produce crash.
Hi, why does this global_mutex minimize the problem?
Now thread A gets until gloabal_mutex init pm and then sleeps. During that time thread B goes to global_mutex and will get e.g. NULL and crash.
Am I missing something?