mt_allocator: static DE-initialization order fiasco?

Carlo Wood carlo@alinoe.com
Tue Oct 12 00:07:00 GMT 2004


On Mon, Oct 11, 2004 at 02:14:07AM +0200, Paolo Carlini wrote:
> |Therefore, if you are getting into serious troubles using __mt_alloc
> directly, the same will happen if you just use std::allocator. Consider
> that mt_allocator is now the default in the build, therefore, if you
> just select the right policy via a typedef in include/ext/mt_allocator.h
> and rebuild the library you can very easily double check what I said.
> 
> Please, if you have a small testcase contribute it.

Ugh that wasn't easy :/.

I used "testsuite/ext/mt_allocator/deallocate_global-4.cc"
as a starting point.  However, I was forced to use more than
one shared library in order to enforce the deinitialization
fiasco to actually happen.

With the attached files, which I attached one by one so that
you can already see them without first saving the tar ball
first, you should be able to reproduce the following.
I also attached everything together as test.tar.bz2 for your
convenience.

$ make
g++-cvs-4.0 -I. -g -shared -o libfoo.so foo.cc memblk.cc
g++-cvs-4.0 -I. -g -shared -o libglobal1.so global1.cc
g++-cvs-4.0 -I. -g -shared -o libglobal2.so global2.cc
g++-cvs-4.0 -I. -g -L. -Wl,-rpath,`pwd` -lglobal1 -lfoo -lglobal2

$ ./a.out
Calling: operator new(258) = 0x804a008
Calling: operator new(100) = 0x804a110
Calling: operator new(4) = 0x804a178
Calling: operator new(4) = 0x804a188
Calling: operator new(4) = 0x804a198
Calling: operator new(4) = 0x804a1a8
Calling: operator new(4) = 0x804a1b8
Calling: operator new(4080) = 0x804a1c8
Calling: operator new(8) = 0x804b1c0
Calling: operator new(258) = 0x804b1d0
Calling: operator new(40) = 0x804b2d8
Calling: operator new(4) = 0x804b308
Calling: operator new(4) = 0x804b318
Calling: operator new(4) = 0x804b328
Calling: operator new(4) = 0x804b338
Calling: operator new(4) = 0x804b348
Calling: operator new(4080) = 0x804b358
Calling: operator new(8) = 0x804c350
Starting destruction.
Destructing object "global1.cc" which is using memory at 0x804b3bc.
This memory belongs to a memory block starting at 0x804b358 with size 4080.
Calling: delete 0x804b308
Calling: delete 0x804b318
Calling: delete 0x804b358  <=== range: [0x804b358 - 0x804c348)
Calling: delete 0x804c350
Calling: delete 0x804b328
Calling: delete 0x804b338
Calling: delete 0x804b348
Calling: delete 0x804b2d8
Calling: delete 0x804b1d0
Destructing object "foo.cc" which is using memory at 0x804b394.
Calling: operator new(4080) = 0x804b1d0
Calling: operator new(8) = 0x804c1c8
terminate called after throwing an instance of 'std::runtime_error'
  what():  Allocation is already freed
Aborted

As you can see, the object "foo.cc" is using memory at 0x804b394
that falls in the range [0x804b358 - 0x804c348) of a memory block
that already freed.

-- 
Carlo Wood <carlo@alinoe.com>

-------------- next part --------------
CXX=g++-cvs-4.0
HEADERS=Global.h string_t.h memblk.h

a.out: libfoo.so libglobal1.so libglobal2.so
	$(CXX) -I. -g -L. -Wl,-rpath,`pwd` -lglobal1 -lfoo -lglobal2

libfoo.so: foo.cc memblk.cc $(HEADERS)
	$(CXX) -I. -g -shared -o libfoo.so foo.cc memblk.cc

libglobal1.so: global1.cc $(HEADERS)
	$(CXX) -I. -g -shared -o libglobal1.so global1.cc
	
libglobal2.so: global2.cc $(HEADERS)
	$(CXX) -I. -g -shared -o libglobal2.so global2.cc
	
clean:
	rm -f *.o *.so a.out

-------------- next part --------------
#ifndef STRING_T_H
#define STRING_T_H

#include <string>
#include <ext/mt_allocator.h>

// This is copied from testsuite/ext/mt_allocator/deallocate_global-4.cc
typedef char char_t;
typedef std::char_traits<char_t> traits_t;
typedef __gnu_cxx::__per_type_pool_policy<char_t, false> pool_t;
typedef __gnu_cxx::__mt_alloc<char_t, pool_t> allocator_t;
typedef std::basic_string<char_t, traits_t, allocator_t> string_t;

#endif // STRING_T_H
-------------- next part --------------
#ifndef MEMBLK_H
#define MEMBLK_H

struct memblk {
  void* ptr;
  size_t size;
};

memblk* find_allocation(void const*);

#endif // MEMBLK_H
-------------- next part --------------
#ifndef GLOBAL_H
#define GLOBAL_H

#include <string>
#include "string_t.h"

struct Global {
  std::string _M_name;
  string_t _M_use_same_pool;
  Global(std::string const& name) : _M_name(name), _M_use_same_pool("XXXX") { }
  ~Global();
};

#endif // GLOBAL_H
-------------- next part --------------
#include <stdexcept>
#include <cassert>
#include "string_t.h"
#include "memblk.h"

static memblk memblocks[100];

memblk* find_allocation(void const* ptr)
{
  memblk* head = memblocks;
  while (head->ptr > ptr || ((char*)head->ptr + head->size) <= (char*)ptr)
  {
    ++head;
    if (head == &memblocks[100])
      throw std::runtime_error("Allocation is already freed");
  }
  return head;
}

void* operator new(size_t size) throw(std::bad_alloc)
{
  printf("Calling: operator new(%d) = ", size);

  void* p = malloc(size);
  if (p == NULL)
    throw std::bad_alloc();

  // Remember the size.
  memblk* head = memblocks;
  while(head->ptr)
    { ++head; }
  head->ptr = p;
  head->size = size;

  printf("%p\n", p);
  return p;
}
 
void operator delete(void* p) throw()
{
  if (p == NULL)
    return;

  // Find the size.
  memblk* head = find_allocation(p);
  assert(head->ptr == p);
  size_t size = head->size;

  bool is_memory_pool_block = (size > 2000);
  printf("Calling: delete %p ", p);
  if (is_memory_pool_block)
    printf(" <=== range: [%p - %p)\n", p, (void*)((char*)p + size)); 
  else
    printf("\n");

  // Destroy contents.
  std::memset(p, 0, size);
  head->ptr = NULL;
  head->size = 0;

  free(p);
}

-------------- next part --------------
#include "Global.h"

Global obj1("global1.cc");

-------------- next part --------------
#include "Global.h"

Global obj2("global2.cc");

-------------- next part --------------
#include <cstdio>
#include "Global.h"
#include "memblk.h"

Global::~Global()
{
  printf("Destructing object \"%s\" which is using memory at %p.\n",
         _M_name.c_str(), _M_use_same_pool.data());
  memblk* head = find_allocation(_M_use_same_pool.data());
  printf("This memory belongs to a memory block "
         "starting at %p with size %d.\n", head->ptr, head->size);
}

static Global obj("foo.cc");

int main()
{
  printf("Starting destruction.\n");
  return 0;
}

-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.tar.bz2
Type: application/x-bzip2
Size: 1612 bytes
Desc: not available
URL: <http://gcc.gnu.org/pipermail/libstdc++/attachments/20041012/2dbe46c1/attachment.bz2>


More information about the Libstdc++ mailing list