[BACK]Return to atexit.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / stdlib

Annotation of src/lib/libc/stdlib/atexit.c, Revision 1.24

1.24    ! pooka       1: /*     $NetBSD: atexit.c,v 1.23 2008/04/28 20:23:00 martin Exp $       */
1.6       thorpej     2:
1.1       cgd         3: /*-
1.14      thorpej     4:  * Copyright (c) 2003 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
1.1       cgd         6:  *
1.14      thorpej     7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Jason R. Thorpe.
1.1       cgd         9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
1.14      thorpej    19:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     21:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     22:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     23:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     25:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     26:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     27:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     28:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     29:  * POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
1.17      lukem      32: #include <sys/cdefs.h>
                     33: #if defined(LIBC_SCCS) && !defined(lint)
1.24    ! pooka      34: __RCSID("$NetBSD: atexit.c,v 1.23 2008/04/28 20:23:00 martin Exp $");
1.17      lukem      35: #endif /* LIBC_SCCS and not lint */
                     36:
1.14      thorpej    37: #include "reentrant.h"
1.1       cgd        38:
1.11      lukem      39: #include <assert.h>
1.1       cgd        40: #include <stdlib.h>
1.14      thorpej    41:
1.1       cgd        42: #include "atexit.h"
1.5       jtc        43:
1.14      thorpej    44: struct atexit_handler {
                     45:        struct atexit_handler *ah_next;
                     46:        union {
                     47:                void (*fun_atexit)(void);
                     48:                void (*fun_cxa_atexit)(void *);
                     49:        } ah_fun;
                     50: #define        ah_atexit       ah_fun.fun_atexit
                     51: #define        ah_cxa_atexit   ah_fun.fun_cxa_atexit
                     52:
                     53:        void *ah_arg;   /* argument for cxa_atexit handlers */
                     54:        void *ah_dso;   /* home DSO for cxa_atexit handlers */
                     55: };
                     56:
                     57: /*
                     58:  * There must be at least 32 to guarantee ANSI conformance, plus
                     59:  * 3 additional ones for the benefit of the startup code, which
                     60:  * may use them to register the dynamic loader's cleanup routine,
                     61:  * the profiling cleanup routine, and the global destructor routine.
                     62:  */
                     63: #define        NSTATIC_HANDLERS        (32 + 3)
                     64: static struct atexit_handler atexit_handler0[NSTATIC_HANDLERS];
                     65:
                     66: #define        STATIC_HANDLER_P(ah)                                            \
                     67:        (ah >= &atexit_handler0[0] && ah < &atexit_handler0[NSTATIC_HANDLERS])
                     68:
                     69: /*
                     70:  * Stack of atexit handlers.  Handlers must be called in the opposite
                     71:  * order they were registered.
                     72:  */
                     73: static struct atexit_handler *atexit_handler_stack;
1.1       cgd        74:
1.13      thorpej    75: #ifdef _REENTRANT
1.14      thorpej    76: /* ..and a mutex to protect it all. */
1.20      xtraeme    77: static mutex_t atexit_mutex;
1.14      thorpej    78: #endif /* _REENTRANT */
1.10      kleink     79:
1.20      xtraeme    80: void   __libc_atexit_init(void) __attribute__ ((visibility("hidden")));
                     81:
1.1       cgd        82: /*
1.14      thorpej    83:  * Allocate an atexit handler descriptor.  If "dso" is NULL, it indicates
                     84:  * a normal atexit handler, which must be allocated from the static pool,
                     85:  * if possible. cxa_atexit handlers are never allocated from the static
                     86:  * pool.
                     87:  *
                     88:  * atexit_mutex must be held.
                     89:  */
                     90: static struct atexit_handler *
                     91: atexit_handler_alloc(void *dso)
                     92: {
                     93:        struct atexit_handler *ah;
                     94:        int i;
                     95:
                     96:        if (dso == NULL) {
                     97:                for (i = 0; i < NSTATIC_HANDLERS; i++) {
                     98:                        ah = &atexit_handler0[i];
1.18      kristerw   99:                        if (ah->ah_atexit == NULL && ah->ah_next == NULL) {
1.14      thorpej   100:                                /* Slot is free. */
                    101:                                return (ah);
                    102:                        }
                    103:                }
                    104:        }
                    105:
                    106:        /*
                    107:         * Either no static slot was free, or this is a cxa_atexit
                    108:         * handler.  Allocate a new one.  We keep the atexit_mutex
                    109:         * held to prevent handlers from being run while we (potentially)
                    110:         * block in malloc().
                    111:         */
                    112:        ah = malloc(sizeof(*ah));
                    113:        return (ah);
                    114: }
                    115:
1.21      xtraeme   116: /*
                    117:  * Initialize atexit_mutex with the PTHREAD_MUTEX_RECURSIVE attribute.
                    118:  * Note that __cxa_finalize may generate calls to __cxa_atexit.
                    119:  */
1.20      xtraeme   120: void
                    121: __libc_atexit_init(void)
                    122: {
                    123:        mutexattr_t atexit_mutex_attr;
                    124:        mutexattr_init(&atexit_mutex_attr);
                    125:        mutexattr_settype(&atexit_mutex_attr, PTHREAD_MUTEX_RECURSIVE);
                    126:        mutex_init(&atexit_mutex, &atexit_mutex_attr);
                    127: }
                    128:
1.14      thorpej   129: /*
                    130:  * Register an atexit routine.  This is suitable either for a cxa_atexit
                    131:  * or normal atexit type handler.  The __cxa_atexit() name and arguments
                    132:  * are specified by the C++ ABI.  See:
                    133:  *
                    134:  *     http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
1.1       cgd       135:  */
                    136: int
1.14      thorpej   137: __cxa_atexit(void (*func)(void *), void *arg, void *dso)
1.1       cgd       138: {
1.14      thorpej   139:        struct atexit_handler *ah;
1.11      lukem     140:
1.14      thorpej   141:        _DIAGASSERT(func != NULL);
1.1       cgd       142:
1.14      thorpej   143:        mutex_lock(&atexit_mutex);
                    144:
                    145:        ah = atexit_handler_alloc(dso);
                    146:        if (ah == NULL) {
                    147:                mutex_unlock(&atexit_mutex);
                    148:                return (-1);
1.1       cgd       149:        }
1.14      thorpej   150:
                    151:        ah->ah_cxa_atexit = func;
                    152:        ah->ah_arg = arg;
                    153:        ah->ah_dso = dso;
                    154:
                    155:        ah->ah_next = atexit_handler_stack;
                    156:        atexit_handler_stack = ah;
                    157:
                    158:        mutex_unlock(&atexit_mutex);
1.1       cgd       159:        return (0);
1.14      thorpej   160: }
                    161:
                    162: /*
                    163:  * Run the list of atexit handlers.  If dso is NULL, run all of them,
                    164:  * otherwise run only those matching the specified dso.
1.15      thorpej   165:  *
                    166:  * Note that we can be recursively invoked; rtld cleanup is via an
                    167:  * atexit handler, and rtld cleanup invokes _fini() for DSOs, which
                    168:  * in turn invokes __cxa_finalize() for the DSO.
1.14      thorpej   169:  */
                    170: void
                    171: __cxa_finalize(void *dso)
                    172: {
1.15      thorpej   173:        static u_int call_depth;
1.14      thorpej   174:        struct atexit_handler *ah, *dead_handlers = NULL, **prevp;
1.15      thorpej   175:        void (*cxa_func)(void *);
                    176:        void (*atexit_func)(void);
                    177:
1.22      xtraeme   178:        mutex_lock(&atexit_mutex);
1.15      thorpej   179:        call_depth++;
1.14      thorpej   180:
1.15      thorpej   181:        /*
                    182:         * If we are at call depth 1 (which is usually the "do everything"
                    183:         * call from exit(3)), we go ahead and remove elements from the
                    184:         * list as we call them.  This will prevent any nested calls from
                    185:         * having to traverse elements we've already processed.  If we are
                    186:         * at call depth > 1, we simply mark elements we process as unused.
                    187:         * When the depth 1 caller sees those, it will simply unlink them
                    188:         * for us.
                    189:         */
1.19      kristerw  190: again:
1.14      thorpej   191:        for (prevp = &atexit_handler_stack; (ah = (*prevp)) != NULL;) {
1.15      thorpej   192:                if (dso == NULL || dso == ah->ah_dso || ah->ah_atexit == NULL) {
                    193:                        if (ah->ah_atexit != NULL) {
1.19      kristerw  194:                                void *p = atexit_handler_stack;
1.15      thorpej   195:                                if (ah->ah_dso != NULL) {
                    196:                                        cxa_func = ah->ah_cxa_atexit;
                    197:                                        ah->ah_cxa_atexit = NULL;
                    198:                                        (*cxa_func)(ah->ah_arg);
                    199:                                } else {
                    200:                                        atexit_func = ah->ah_atexit;
                    201:                                        ah->ah_atexit = NULL;
                    202:                                        (*atexit_func)();
                    203:                                }
1.19      kristerw  204:                                /* Restart if new atexit handler was added. */
                    205:                                if (p != atexit_handler_stack)
                    206:                                        goto again;
1.14      thorpej   207:                        }
1.15      thorpej   208:
                    209:                        if (call_depth == 1) {
                    210:                                *prevp = ah->ah_next;
1.18      kristerw  211:                                if (STATIC_HANDLER_P(ah))
                    212:                                        ah->ah_next = NULL;
                    213:                                else {
1.15      thorpej   214:                                        ah->ah_next = dead_handlers;
                    215:                                        dead_handlers = ah;
                    216:                                }
                    217:                        } else
                    218:                                prevp = &ah->ah_next;
1.14      thorpej   219:                } else
                    220:                        prevp = &ah->ah_next;
                    221:        }
1.16      nathanw   222:        call_depth--;
1.24    ! pooka     223:        mutex_unlock(&atexit_mutex);
1.16      nathanw   224:
                    225:        if (call_depth > 0)
1.15      thorpej   226:                return;
1.14      thorpej   227:
                    228:        /*
                    229:         * Now free any dead handlers.  Do this even if we're about to
                    230:         * exit, in case a leak-detecting malloc is being used.
                    231:         */
                    232:        while ((ah = dead_handlers) != NULL) {
                    233:                dead_handlers = ah->ah_next;
                    234:                free(ah);
                    235:        }
                    236: }
                    237:
                    238: /*
                    239:  * Register a function to be performed at exit.
                    240:  */
                    241: int
                    242: atexit(void (*func)(void))
                    243: {
                    244:
                    245:        return (__cxa_atexit((void (*)(void *))func, NULL, NULL));
1.1       cgd       246: }

CVSweb <webmaster@jp.NetBSD.org>