diff options
Diffstat (limited to 'stdlib/cxa_atexit.c')
-rw-r--r-- | stdlib/cxa_atexit.c | 73 |
1 files changed, 50 insertions, 23 deletions
diff --git a/stdlib/cxa_atexit.c b/stdlib/cxa_atexit.c index a3d4c5037d..3bdf871e53 100644 --- a/stdlib/cxa_atexit.c +++ b/stdlib/cxa_atexit.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1999, 2001, 2002 Free Software Foundation, Inc. +/* Copyright (C) 1999, 2001, 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -16,9 +16,13 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#include <bits/libc-lock.h> +#include <assert.h> #include <stdlib.h> + +#include <bits/libc-lock.h> #include "exit.h" +#include <atomic.h> +#include <sysdep.h> #undef __cxa_atexit @@ -33,10 +37,14 @@ __cxa_atexit (void (*func) (void *), void *arg, void *d) if (new == NULL) return -1; - new->flavor = ef_cxa; +#ifdef PTR_MANGLE + PTR_MANGLE (func); +#endif new->func.cxa.fn = (void (*) (void *, int)) func; new->func.cxa.arg = arg; new->func.cxa.dso_handle = d; + atomic_write_barrier (); + new->flavor = ef_cxa; return 0; } INTDEF(__cxa_atexit) @@ -48,49 +56,68 @@ __libc_lock_define_initialized (static, lock) static struct exit_function_list initial; struct exit_function_list *__exit_funcs = &initial; +uint64_t __new_exitfn_called; struct exit_function * __new_exitfn (void) { + struct exit_function_list *p = NULL; struct exit_function_list *l; + struct exit_function *r = NULL; size_t i = 0; __libc_lock_lock (lock); - for (l = __exit_funcs; l != NULL; l = l->next) + for (l = __exit_funcs; l != NULL; p = l, l = l->next) { - for (i = 0; i < l->idx; ++i) - if (l->fns[i].flavor == ef_free) + for (i = l->idx; i > 0; --i) + if (l->fns[i - 1].flavor != ef_free) break; - if (i < l->idx) + + if (i > 0) break; - if (l->idx < sizeof (l->fns) / sizeof (l->fns[0])) - { - i = l->idx++; - break; - } + /* This block is completely unused. */ + l->idx = 0; } - if (l == NULL) + if (l == NULL || i == sizeof (l->fns) / sizeof (l->fns[0])) { - l = (struct exit_function_list *) - malloc (sizeof (struct exit_function_list)); - if (l != NULL) + /* The last entry in a block is used. Use the first entry in + the previous block if it exists. Otherwise create a new one. */ + if (p == NULL) { - l->next = __exit_funcs; - __exit_funcs = l; + assert (l != NULL); + p = (struct exit_function_list *) + calloc (1, sizeof (struct exit_function_list)); + if (p != NULL) + { + p->next = __exit_funcs; + __exit_funcs = p; + } + } - l->idx = 1; - i = 0; + if (p != NULL) + { + r = &p->fns[0]; + p->idx = 1; } } + else + { + /* There is more room in the block. */ + r = &l->fns[i]; + l->idx = i + 1; + } /* Mark entry as used, but we don't know the flavor now. */ - if (l != NULL) - l->fns[i].flavor = ef_us; + if (r != NULL) + { + r->flavor = ef_us; + ++__new_exitfn_called; + } __libc_lock_unlock (lock); - return l == NULL ? NULL : &l->fns[i]; + return r; } |