
Pool's fast_pool_allocator class uses singleton_pool to manage a single underlying pool based on the requested size. Singleton_pool in turn uses singleton_default (found in
which tries _really_ hard to force the compiler to construct the controlled instance at just the right time.
What's happening in your test case is that the namespace-scoped owned_base::pool instance (line 187 in sh_owned_base_nt.hpp) is actually being constructed by the compiler _before_
initializer object (called create_object, at line 95 in
results in the compiler destructing the singleton pool _before_ destructing your owned_base::pool instance. Thus the memory containing the list nodes of the two std::list instanced contained in owned_base::pool is has been freed by the time the list destructors run.
I'm a little hard-pressed to make a call as to whether the problem is with GCC's initialization ordering or whether the "cleverness" in pool/detail/singleton.hpp is simply bankrupt and is trying to guarantee something which the language standard cannot.
The namespace-scoped initializer generated in pool/detail/singleton.hpp lexically precedes the declaration of the client in sh_owned_base_nt.hpp. Since this test case is a single translation unit, this should in theory ensure that the former is constructed before the latter. However, there's some indirection and function-scoped static objects in play here, too, which may render that guarantee meaningless.
I'd be interested to get some additional eyes on the code in
Chris Newbold <Chris.Newbold <at> mathworks.com> writes: pool/detail/singleton.hpp) the namespace-scoped pool/detail/singleton.hpp). That in turn pool/detail/singleton.hpp (there's a decent
comment there about how it's supposed to work) and see if there can be a reasonable expectation for it to work on a conforming compiler...
Hello, I did look hard into pool/detail/singleton.hpp in the past because it deals with a problem that I also had to face in the implementation of Boost.Flyweight. These are my conclusions wrt to pool/detail/singleton.hpp and with respect to the particular problem you're now studying: * pool/detail/singleton.hpp claims that singleton_default<T>::instance() is automatically called before main() if no user code does it explicitly. Strictly speaking, the implementation does not guarantee this, but only that singleton_default<T>::instance() will be called during the so-called dynamic initialization phase of the program startup sequence. For all practical purposes, however, this is equivalent to the orignal claim, so the problem does not lie here. * Where the problem lies can be found by looking at the usage of singleton_pool by fast_pool_allocator: notice that singleton_pool is used *only* when fast_pool_allocator::allocate or ::deallocate are invoked. So, it is perfectly possible to construct a fast_pool_allocator without that forcing the compiler to construct the underying singleton_pool *yet*. And this is what's happening indeed, the sequence of objects construction we're having is this: 1. owned_base::pool_ begins construction 1.1 call of new pool_lii() inside pool::pool 1.1.1 a fast_pool_allocator is cted inside pool_lii ctor. 1.2 pool_ii ends construction 2. pool_ ends construction 3. the singleton instance associated to singleton_pool<...>is cted before main() because fast_pool_allocator uses it later (singleton guarantee). This sequence is consistent with the guarantees provided by singleton_default<T>. The problem lies in the design of fast_pool_allocator: as it stands, the construction of a fast_pool_allocator does *not* force the prior construction of the internal singleton instance, and this is a mistake. If my analysis is correct, to fix the problem one need only ensure that his construction order is preserved for instance by explicitly using singleton_pool<...> inside fast_pool_allocator ctors like this: fast_pool_allocator() { singleton_pool<fast_pool_allocator_tag, sizeof(T), UserAllocator, Mutex, NextSize>::is_from(0); } template <typename U> fast_pool_allocator( const fast_pool_allocator<U, UserAllocator, Mutex, NextSize> &) { singleton_pool<fast_pool_allocator_tag, sizeof(T), UserAllocator, Mutex, NextSize>::is_from(0); } If you try this please let me know the result. Hope this helps, Joaquín M López Muñoz Telefónica, Investigación y Desarrollo