This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
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...