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


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

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   `------------------------


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