This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: global {con,de}structors, atexit & friends
Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de> writes:
> How so? At the moment, the ELF interpreter loops over the shared
> objects and invokes the .fini code, which in turn loops over tables
> and calls destructors. Using lists instead of vectors can't add that
> much overhead.
I don't know whether you mean the same design we came up. If yes,
you're right, it is not much slower.
Here's what I think we should do:
- drop __DTOR_LIST__; this cannot work
- for every object which needs a destructor we'll add (in place of the
code to add to the __DTOR_LIST__ list something like this):
.section .bss
Lunique:.long 0
.long 0
.previous
I.e., we add two words. One word will contain the pointer to the
destructor, the other word will be a link to the next entry.
- the code generated for a constructor of a global of static function
locale object will include a call to a new function __register_destructor
which takes two arguments:
void __register_destructor (void (*destructor) (void), void *slot)
The implementation of this function will be something like this:
struct destrlist
{
void (*destr) (void);
struct destrlist *next;
};
static struct destrlist *last_destr;
void
__register_destructor (void (*destructor) (void), void *slot)
{
struct destrlist *tslot = (struct destrlist *) slot;
tslot->destr = destructor;
// ***ATOMIC
tlost->next = last_destr;
last_destr = tslot;
// ***ATOMIC
}
The region between the ***ATOMIC must be carefully designed and will
contain some assembler magic to work even in multi-threaded programs.
The same file will contain a function which contains a function to run
through the list and call the destructors:
void
__run_destructors (void)
{
while (last_destr != NULL)
{
void (*tmp) (void) = last_destr->destr;
last_destr = last_destr->next;
(*tmp) ();
}
}
What do we gain:
- we always get the correct destructor order since the order of destructors
__run_destructors sees is determined dynamically.
(Yes Jonathan, it even works in your situation :-)
- given that we can handled the atomic operations correctly this works
for all programs.
- we have one list, even if the program is linked against shared objects.
- it's fast enough since the call to register the destructors does not
require dynamically allocating memory; walking through the linked list
at program termination also is fast enough
- this is implementable for all systems (please note that this still is
a method to use in addition to the atexit() method; atexit() still
must be used if 100% standard compliance is wanted)
We are left with only one problem: unloadable shared objects. I can
see a solution for this as well but it becomes *really* ugly in the
presence of threads.
Before unmapping the memory for such a shared object all the elements
of the `last_destr' list pointing to functions in the shared object
must be removed. I'd propose the following method (keep in mind that
a shared object can be loaded, unloaded, loaded again, unloaded
again):
- keep a list of all possible destructors in the object
(i.e., keep __DTOR_LIST__, extend it with all destructors
for function local objects)
- the .fini function of the shared object compares for each
pointer in the `last_destr' list whether it is one of the
known destructors.
- If yes, it removed it from the list
The last point is the tricky one. Since the memory in which which the
`last_destr' element uses will not be available after unloading we
cannot simply mark the entry as unusable, we actually have to remove
it. This is a problem in multi-threaded program and especially if you
see that there might be multi threads unloading different modules at
the same time.
If somebody can provide a good way to implement this I'd be glad. I'm
not sure what this whole design is worth without solving this dynamic
loading problem.
--
---------------. drepper at gnu.org ,-. 1325 Chesapeake Terrace
Ulrich Drepper \ ,-------------------' \ Sunnyvale, CA 94089 USA
Cygnus Solutions `--' drepper at cygnus.com `------------------------