Solving mt_allocator's static initialization ordering problem
Brad Spencer
spencer@infointeractive.com
Mon Jun 28 18:29:00 GMT 2004
On Thu, May 06, 2004 at 12:11:14PM -0300, Brad Spencer wrote:
> I've been trying to the (trunk) mt_allocator within the gcc-3.4.0
> sources (in another email thread) for alignment reasons on SPARC, and
> so far, I haven't had problems there. However, on i686-pc-linux-gnu,
> I've encountered problems with static initialization of the
> mt_allocator's _S_options structure.
>
> For example, using gdb on the following small program will reveal that
> when _S_initialize() runs on line 12, _S_options' constructor appears
> to have not been run. (I don't have a lot of luck setting breakpoints
> on such things, so I am not 100% positive.)
>
> #include <memory>
>
> std::allocator<int *> alloc;
> struct init
> {
> init()
> {
> // Here, alloc._S_options is not initialized, so the constructor
> // makes a broken allocator.
> alloc.allocate(1);
> }
>
> } ini;
>
> int
> main()
> {
> std::allocator<void *> alloc2;
>
> // Here, this _S_options is initialized.
> alloc2.allocate(1);
>
> return 0;
> }
I finally got back to this issue, and started using the head of the
gcc-3.4-branch, which includes the latest mt_allocator changes. The
problem was still happening, but I managed to prove to myself that it
was static initialization order. The nub of the problem is that _Tune
is a class, so its members can't have values before
__static_initialization_and_destruction_0() runs. It looks like this
happens too late:
Breakpoint 2, init (this=0x2ea81) at foo.cc:10
10 alloc.allocate(1);
(gdb) p __gnu_cxx::__mt_alloc<int*>::_S_options
$2 = {
_M_align = 0,
_M_max_bytes = 0,
_M_min_bin = 0,
_M_chunk_size = 0,
_M_max_threads = 0,
_M_freelist_headroom = 0,
_M_force_new = false
}
I tried applying __attribute__((init_priority (101))) in several
places, but those that the compiler accepted seemed to have no
effect. The assembly for __static_initialization_and_destruction_0()
still showed init::init() running before _Tune::Tune().
All I could come up with was this hack of looking to see if
_S_options is still uninitialized in _S_initialize(), and if so,
running an in-place construction to use the default values.
It's ugly, but it does work. Is it safe?
I didn't go any further with change because I hit upon a couple of
questions that I couldn't answer:
1. When exactly are are you allowed to call _S_set_options() and what
exactly will it do? It would seem you have to be after the static
initialization of _S_options, but before the first allocation.
Tricky.
2. Is there a cleaner way to do the same thing without reworking how
the tunable parameters are set? Perhaps things could be changed so
_S_options is _only_ initialized by this lazy init? This
probably depends on the answer to question 1.
--
------------------------------------------------------------------
Brad Spencer - spencer@infointeractive.com - "It's quite nice..."
Systems Architect | InfoInterActive Corp. | A Canadian AOL Company
-------------- next part --------------
Index: mt_allocator.h
===================================================================
RCS file: /cvsroot/gcc/gcc/libstdc++-v3/include/ext/mt_allocator.h,v
retrieving revision 1.31
diff -u -p -r1.31 mt_allocator.h
--- mt_allocator.h 18 Jun 2004 23:27:30 -0000 1.31
+++ mt_allocator.h 28 Jun 2004 17:34:05 -0000
@@ -491,6 +491,15 @@ namespace __gnu_cxx
if (_S_options._M_force_new)
return;
+ // Ensure that the static initialization of _S_options has happened.
+ // This is frighteningly ugly, and depends on (a) _M_align == 0 being an
+ // invalid value that is only present at startup, and (b) the real
+ // static initialization that happens later not actually changing
+ // anything.
+ if(_S_options._M_align == 0) {
+ new (&_S_options) _Tune;
+ }
+
// Calculate the number of bins required based on _M_max_bytes.
// _S_bin_size is statically-initialized to one.
size_t __bin_size = _S_options._M_min_bin;
More information about the Libstdc++
mailing list