
I was testing my own "shifted_ptr_test2.cpp" on RHEL 4.5 using Gcc 3.4.6 and the following object declared in "sh_owned_base_nt.hpp" caused a segmentation violation inside its destructor. It doesn't seem to happen on Windows, just Linux: pool owned_base::pool_; // derives from boost::pool<> Thanks, -Phil

From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Phil Bouchard Sent: Thursday, July 03, 2008 2:36 AM
I was testing my own "shifted_ptr_test2.cpp" on RHEL 4.5 using Gcc 3.4.6 and the following object declared in "sh_owned_base_nt.hpp" caused a segmentation violation inside its destructor. It doesn't seem to happen on Windows, just Linux:
pool owned_base::pool_; // derives from boost::pool<>
Greetings; I've just assumed maintainership for Boost.Pool, so please bear with me as I work through the code. I can't see any obvious reason why this would be failing. Defining static data members (i.e. owned_base::pool) in a header file always makes me a little nervous, though. I've seen too many cases where compilers foul up the ODR under such conditions; GCC 3.4.6 is a bit old... Could you provide a stack trace of the crash? That would make it easier to get some traction on what might be going wrong here. Thanks. -Chris

"Chris Newbold" <Chris.Newbold@mathworks.com> wrote in message news:6F6A2FC198A0F943ACC2259C38A2E30B6688EDD1A8@EXCHANGE-AH.ad.mathworks.com... [...]
Greetings; I've just assumed maintainership for Boost.Pool, so please bear with me as I work through the code.
I can't see any obvious reason why this would be failing. Defining static data members (i.e. owned_base::pool) in a header file always makes me a little nervous, though. I've seen too many cases where compilers foul up the ODR under such conditions; GCC 3.4.6 is a bit old...
Could you provide a stack trace of the crash? That would make it easier to get some traction on what might be going wrong here. Thanks.
I get the following backtrace, but I haven't investiguated thta much yet. It looks to be an STL problem but I'll check this out tomorrow: #0 0x0804fc7a in std::_List_base<std::pair<char*, char*>, boost::fast_pool_allocator<std::pair<char*, char*>, boost::default_user_allocator_new_delete, boost::details::pool::pthread_mutex, 32u> >::_M_clear ( this=0x8dc2008) at /usr/include/c++/4.3/bits/list.tcc:77 #1 0x0804fcdb in ~_List_base (this=0x8dc2008) at /usr/include/c++/4.3/bits/stl_list.h:358 #2 0x0804fcef in ~list (this=0x8dc2008) at /usr/include/c++/4.3/bits/stl_list.h:417 #3 0x0804fd4f in ~auto_ptr (this=0x80685b8) at /usr/include/c++/4.3/backward/auto_ptr.h:173 #4 0x0804fe80 in ~pool (this=0x80685a0) at ../../../boost/detail/sh_owned_base_nt.hpp:71 #5 0x006bc527 in exit () from /lib/tls/libc.so.6 #6 0x006a6ded in __libc_start_main () from /lib/tls/libc.so.6 #7 0x08048df1 in _start () -Phil

From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Phil Bouchard Sent: Sunday, July 06, 2008 3:46 PM
I get the following backtrace, but I haven't investiguated thta much yet. It looks to be an STL problem but I'll check this out tomorrow:
#0 0x0804fc7a in std::_List_base<std::pair<char*, char*>, boost::fast_pool_allocator<std::pair<char*, char*>, boost::default_user_allocator_new_delete, boost::details::pool::pthread_mutex, 32u> >::_M_clear ( this=0x8dc2008) at /usr/include/c++/4.3/bits/list.tcc:77 #1 0x0804fcdb in ~_List_base (this=0x8dc2008) at /usr/include/c++/4.3/bits/stl_list.h:358 #2 0x0804fcef in ~list (this=0x8dc2008) at /usr/include/c++/4.3/bits/stl_list.h:417 #3 0x0804fd4f in ~auto_ptr (this=0x80685b8) at /usr/include/c++/4.3/backward/auto_ptr.h:173 #4 0x0804fe80 in ~pool (this=0x80685a0) at ../../../boost/detail/sh_owned_base_nt.hpp:71 #5 0x006bc527 in exit () from /lib/tls/libc.so.6 #6 0x006a6ded in __libc_start_main () from /lib/tls/libc.so.6 #7 0x08048df1 in _start ()
Just for grins, try printing a message out from the destructor of owned_base::pool (or setting a breakpoint there) and see how many times you wind up there. My hunch is that this is an ODR issue... -Chris

"Chris Newbold" <Chris.Newbold@mathworks.com> wrote in message news:6F6A2FC198A0F943ACC2259C38A2E30B6688EDD1B4@EXCHANGE-AH.ad.mathworks.com... [...]
Just for grins, try printing a message out from the destructor of owned_base::pool (or setting a breakpoint there) and see how many times you wind up there. My hunch is that this is an ODR issue...
I get the following: ... virtual boost::detail::sh::owned_base::~owned_base(): 1997 virtual boost::detail::sh::owned_base::~owned_base(): 1998 virtual boost::detail::sh::owned_base::~owned_base(): 1999 0 1 1 virtual boost::detail::sh::owned_base::~owned_base(): 2000 1 test = 5 virtual boost::detail::sh::owned_base::~owned_base(): 2001 0 0 Segmentation fault BTW I have gotten the same problem by using Gcc 4.3 and I am not linking multiple source files. Here is my build process: $ g++ shifted_ptr_test2.cpp -I ../../.. Regards, -Phil

From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Phil Bouchard Sent: Tuesday, July 08, 2008 1:46 AM
"Chris Newbold" <Chris.Newbold@mathworks.com> wrote in message news:6F6A2FC198A0F943ACC2259C38A2E30B6688EDD1B4@EXCHANGE- AH.ad.mathworks.com...
[...]
Just for grins, try printing a message out from the destructor of owned_base::pool (or setting a breakpoint there) and see how many times you wind up there. My hunch is that this is an ODR issue...
I get the following:
...
BTW I have gotten the same problem by using Gcc 4.3 and I am not linking multiple source files. Here is my build process: $ g++ shifted_ptr_test2.cpp -I ../../..
I've been able to reproduce this failure in my local sandbox, using GCC 4.1.2. My initial ODR hypothesis was not right, but was close: we're having a static-initialization ordering issue. 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 pool/detail/singleton.hpp) 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_ the namespace-scoped initializer object (called create_object, at line 95 in pool/detail/singleton.hpp). That in turn 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 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... -Chris

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

From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Joaquin M Lopez Munoz Sent: Tuesday, July 08, 2008 6:06 PM
Thanks for your detailed analysis, Joaquin. I have a few comments/questions which are interspersed below...
* 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.
Right. The way that singleton_default guarantees that instance() is called during dynamic initialization is by declaring a global object-- an instance of singleton_default<T>::object_creator (around line 95 of singleton.hpp). The controlled instance is created by the first of: 1. A call to instance() from application code attempting to use the pool or 2. The construction (during dynamic initialization) of object_creator.
* 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).
I'm not sure I buy this explanation entirely. owned_base::pool_ is a global object. As described above, so is object_creator. The test case consists of a single translation unit, and inside that translation unit, the declaration of object_creator precedes that of owned_base::pool_ (because object_creator is declared by a Boost.Pool header which is included before the application-level code which declares owned_base). Following the fairly clear rules about initialization order for namespace-scoped global objects, I would have expected the compiler to construct object_creator first (and therefore the pool) before constructing owned_base::pool_. I'm not at all convinced that singleton_default or fast_pool_allocator is actually correct, but I do think this situation is a bit more complex... -Chris
participants (3)
-
Chris Newbold
-
Joaquin M Lopez Munoz
-
Phil Bouchard