This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: mt_allocator: static DE-initialization order fiasco?


On Thu, Oct 14, 2004 at 03:50:55PM +0200, Paolo Carlini wrote:
> Will you keep on testing the allocator and reporting ASAP any problem 
> you will encounter?

I am not as active anymore in this field (only when releasing a new
version of libcwd I sync with gcc CVS again and run the full testsuite;
I am currently not using libcwd in an active project myself and the
amount of feedback from others is minimal: I do not expect to do new
releases on a regular basis at all).

Moreover, I "solved" my problem before even mailing this list by
avoiding the destruction of the pools myself; the current libcwd code
is therefore insensible to this.  Normally I would wait until you
made a more permanent change, and mt_alloc stabelizes a bit, before
I'd test a situation again in which I allow libstdc++ to clean up
the memory pool (that would have been after the release of 4.0).

Therefore, let me propose that as soon as you are satisfied with
mt_alloc and you think that it will clean up correctly - then add
a post to this thread (or mail me in private), asking me to test it.

-- 
Carlo Wood <carlo@alinoe.com>

PS The only extra complication that libcwd adds is that it uses
   a map<> to administrate the memory allocations: at the very
   moment that ~pool() is called for "non-internal allocations"
   (== allocations that are actually stored in that map), this
   map still has to work (~pool calls delete (for the memory pool
   itself)).  That requires therefore that the pool that is
   instantiated in libstdc++ (from mt_allocator.cc) has to be
   destructed BEFORE the custom pool that will be instantiated in
   libcwd.so.  I think this is a special requirement that
   at first sight is counter intuitive.  I first tried to just
   not destruct the pool for internal allocations (thus making
   that map<> continue to work) but that failed as well (and also
   while inside ~pool(); I forgot why :(, but I know that my
   conclusion was that I had to stop destructing any pool whatsoever,
   at which point I mailed the list.
   The main disadvantage of my current approach is that in
   order to control whether or not a pool is destructed, I had
   to write my own policy class - which means that I am using
   a rather large interface that is not standarized now.
   That means probably more #ifdef's in the future... but
   well - I have it isolated pretty good ;) - libstdc++
   can impossibly be changed more often than it has done in
   the past in this regard; nevertheless its messy :(.  Look
   this piece of code that provides the interface to the
   allocators of libstdc++ (version 3.x and higher) that I need:

[...]
#if __GNUC__ == 3 && __GNUC_MINOR__ < 4
template<bool needs_lock, int pool_instance>
  struct CharPoolAlloc : public std::__default_alloc_template<needs_lock, random_salt + pool_instance> {
    typedef char* pointer;
  };
#elif __GNUC__ == 3 && __GNUC_MINOR__ == 4 && __GNUC_PATCHLEVEL__ == 0
template<bool needs_lock, int pool_instance>
  struct CharPoolAlloc : public __gnu_cxx::__pool_alloc<needs_lock, random_salt + pool_instance> {
    typedef char* pointer;
  };
#else // gcc 3.4.1 and higher.
template<int pool_instance>
  struct char_wrapper {
    char c;
  };
#if __GNUC__ == 3
// gcc 3.4.1 and 3.4.2 always use a lock, in the threaded case.
template<bool needs_lock, int pool_instance>
  class CharPoolAlloc : public __gnu_cxx::__pool_alloc<char_wrapper<pool_instance> > { };
#else // gcc 4.0 and higher.
// A wrapper around a pointer to the actual pool type (__gnu_cxx::__pool<>).
// This class does not need a reference counter because we only use it for static objects.
template<class __pool_type>
  struct static_pool_instance {
    __pool_type* ptr;
    bool M_internal;
    static_pool_instance(void) { }	// Do NOT initialize `ptr'! It is automatically initialized
    					// to NULL because this is a static POD object and ptr might
					// already be initialized before the global constructor of
					// this object would be called.
    void create(void);			// This does the initialization.
  };
// The class that holds the actual pool instance.
// This class also defines policies (so far only whether or not threading is used).
template<int pool_instance, bool needs_lock>
  struct pool_instance_and_policy {
    typedef __gnu_cxx::__pool<needs_lock> __pool_type;		// Underlaying pool type.
    static static_pool_instance<__pool_type> _S_pool_instance;	// The actual pool instance.

    // The following is needed as interface of a 'pool_policy' class as used by __gnu_cxx::__mt_alloc.
    static __pool_type& _S_get_pool(void)			// Accessor to the __pool_type singleton.
        { return *_S_pool_instance.ptr; }
    static void _S_initialize_once(void) 			// This is called every time a new allocation is done.
    { 
      static bool __init;
      if (__builtin_expect(__init == false, false))
      {
        _S_pool_instance.create();
	_S_pool_instance.ptr->_M_initialize_once(); 
	__init = true;
      }
    }
  };

#ifdef __GTHREADS
// Specialization, needed because in this case more interface is needed for the
// 'pool_policy' class as used by __gnu_cxx::__mt_alloc.
template<int pool_instance>
  struct pool_instance_and_policy<pool_instance, true>
  {
    typedef __gnu_cxx::__pool<true> __pool_type;		// Underlaying pool type.
    static static_pool_instance<__pool_type> _S_pool_instance;	// The actual pool instance.
    
    // The following is needed as interface of a 'pool_policy' class as used by __gnu_cxx::__mt_alloc.
    static __pool_type& _S_get_pool(void)			// Accessor to the __pool_type singleton.
        { return *_S_pool_instance.ptr; }
    static void _S_initialize_once(void) 			// This is called every time a new allocation is done.
    { 
      static bool __init;
      if (__builtin_expect(__init == false, false))
      {
        _S_pool_instance.create();
	_S_pool_instance.ptr->_M_initialize_once(_S_initialize);	// Passes _S_initialize in this case.
	__init = true;
      }
    }
    // And the extra interface:
    static void _S_destroy_thread_key(void* __freelist_pos) { _S_get_pool()._M_destroy_thread_key(__freelist_pos); }
    static void _S_initialize(void) { _S_get_pool()._M_initialize(_S_destroy_thread_key); }
  };

template<int pool_instance>
  static_pool_instance<typename pool_instance_and_policy<pool_instance, true>::__pool_type>
      pool_instance_and_policy<pool_instance, true>::_S_pool_instance;
#endif // __GTHREADS

template<int pool_instance, bool needs_lock>
  static_pool_instance<typename pool_instance_and_policy<pool_instance, needs_lock>::__pool_type>
      pool_instance_and_policy<pool_instance, needs_lock>::_S_pool_instance;

template<bool needs_lock, int pool_instance>
  class CharPoolAlloc : public __gnu_cxx::__mt_alloc<char, pool_instance_and_policy<pool_instance, needs_lock> > { };
#endif // gcc 4.0 and higher.
#endif // gcc 3.4.1 and higher.

[...]
// Use CharPoolAlloc here...


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]