Quick Allocator Static Destruction Bug

I have encountered a bug when using BOOST_SP_USE_QUICK_ALLOCATOR with shared_ptr<> objects that are contained in a static container. This bug is verifiable with Visual Studio 2005 SP1 as well as MinGW GCC 4.2.1. As an example, I have created a static std::map, which pairs a long with a boost::shared_ptr<>. This is the scenario where I first encountered the bug. The boost preprocessor definition BOOST_SP_USE_QUICK_ALLOCATOR is defined. When the program exists, the application crashes during deallocation of the shared_ptrs. Under Visual Studio 2005, MSVCR80d.dllcrashes during _free_dbg() (and likewise MSVCR80.dll crashes during _free()). If the map is made a regular member instead of a static member, the program exits properly. In addition, if the boost quick allocator is disabled, the program exits properly. In my testing, I have found the key type is irrelevant, and what the shared_ptr points to also does not matter. The issue is with the Quick Allocator and the runtime cleanup of static objects. Here is an example console app. that will produce the problem. ------------------- // Boost Headers #define BOOST_SP_USE_QUICK_ALLOCATOR #include <boost/shared_ptr.hpp> // STL Map #include <map> // Test struct for Shared Pointer struct tTestItem { long x; }; // Test Class to use static map class tTestClass { public: tTestClass(long id) { boost::shared_ptr<tTestItem> xTestItem(new tTestItem()); m_TestMap.insert(std::make_pair<long, boost::shared_ptr<tTestItem>
(id, xTestItem)); }
private: static std::map<long, boost::shared_ptr<tTestItem> > m_TestMap; }; // Create Static Instance std::map<long, boost::shared_ptr<tTestItem> > tTestClass::m_TestMap; // Simple Main App int main() { tTestClass class1(1); tTestClass class2(2); return 0; } ------------------- Under Visual Studio, the problem can be detected as follows: In boost\detail\lwm_win32_cs.hpp, put breakpoints in (1) lightweight_mutex's ctor, (2) lightweight_mutex's dtor, and (3) scoped_lock's ctor. In boost\detail\quick_allocator.hpp, put breakpoints in allocator_impl<size, align_>'s (4) mutex(), (5) alloc(), and (6) dealloc(). Run the program. (5) [alloc()] fires. We're constructing class1, which is constructing a shared_ptr. (4) [mutex()] fires, as alloc() calls mutex(). (1) [lightweight_mutex's ctor] fires, as mutex() constructs a static lightweight_mutex. (3) [scoped_lock's ctor] fires, as alloc() then constructs a scoped_lock from the static lightweight_mutex. (5), (4), and (3) then fire as we construct class2 (the static lightweight_mutex has already been constructed, so (1) doesn't run again.) (2) [lightweight_mutex's dtor] fires, as we exit main() and destroy the static lightweight_mutex. (6) [dealloc()] fires, as we exit main() and destroy the static map and its contained shared_ptrs. (4) [mutex()] fires, returning a reference to the destroyed static lightweight_mutex. (3) [scoped_lock's ctor] fires, which calls EnterCriticalSection() with a trashed CRITICAL_SECTION, which causes the program to crash. Please let me know if I am misusing boost functionality in the above test application. Sincerely, Matthew Hunt

Matt Hunt:
I have encountered a bug when using BOOST_SP_USE_QUICK_ALLOCATOR with shared_ptr<> objects that are contained in a static container. This bug is verifiable with Visual Studio 2005 SP1 as well as MinGW GCC 4.2.1.
As an example, I have created a static std::map, which pairs a long with a boost::shared_ptr<>. This is the scenario where I first encountered the bug. The boost preprocessor definition BOOST_SP_USE_QUICK_ALLOCATOR is defined. When the program exists, the application crashes during deallocation of the shared_ptrs.
This is indeed a problem caused by destruction order. The static map is initialized before the mutex, and is therefore destroyed after it. You should be able to work around the issue by adding a dummy static boost::shared_ptr<tTestItem> p( (tTestItem*)0 ); before the map, ensuring that the quick_allocator will be invoked before the map construction, initializing the mutex. Another (more time-consuming) option is to roll your own "quick allocator" that doesn't suffer from initialization order problems and use the allocator constructor of shared_ptr.
participants (2)
-
Matt Hunt
-
Peter Dimov