[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.16.6.1

1.16.6.1! bouyer      1: /*     $NetBSD$        */
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:  * 3. All advertising materials mentioning features or use of this software
                     19:  *    must display the following acknowledgement:
1.14      thorpej    20:  *        This product includes software developed by the NetBSD
                     21:  *        Foundation, Inc. and its contributors.
                     22:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     23:  *    contributors may be used to endorse or promote products derived
                     24:  *    from this software without specific prior written permission.
1.1       cgd        25:  *
1.14      thorpej    26:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     27:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     28:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     29:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     30:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     31:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     32:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     33:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     34:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     35:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     36:  * POSSIBILITY OF SUCH DAMAGE.
                     37:  */
                     38:
                     39: #include "reentrant.h"
1.1       cgd        40:
1.11      lukem      41: #include <assert.h>
1.1       cgd        42: #include <stdlib.h>
1.14      thorpej    43:
1.1       cgd        44: #include "atexit.h"
1.5       jtc        45:
1.14      thorpej    46: struct atexit_handler {
                     47:        struct atexit_handler *ah_next;
                     48:        union {
                     49:                void (*fun_atexit)(void);
                     50:                void (*fun_cxa_atexit)(void *);
                     51:        } ah_fun;
                     52: #define        ah_atexit       ah_fun.fun_atexit
                     53: #define        ah_cxa_atexit   ah_fun.fun_cxa_atexit
                     54:
                     55:        void *ah_arg;   /* argument for cxa_atexit handlers */
                     56:        void *ah_dso;   /* home DSO for cxa_atexit handlers */
                     57: };
                     58:
                     59: /*
                     60:  * There must be at least 32 to guarantee ANSI conformance, plus
                     61:  * 3 additional ones for the benefit of the startup code, which
                     62:  * may use them to register the dynamic loader's cleanup routine,
                     63:  * the profiling cleanup routine, and the global destructor routine.
                     64:  */
                     65: #define        NSTATIC_HANDLERS        (32 + 3)
                     66: static struct atexit_handler atexit_handler0[NSTATIC_HANDLERS];
                     67:
                     68: #define        STATIC_HANDLER_P(ah)                                            \
                     69:        (ah >= &atexit_handler0[0] && ah < &atexit_handler0[NSTATIC_HANDLERS])
                     70:
                     71: /*
                     72:  * Stack of atexit handlers.  Handlers must be called in the opposite
                     73:  * order they were registered.
                     74:  */
                     75: static struct atexit_handler *atexit_handler_stack;
1.1       cgd        76:
1.13      thorpej    77: #ifdef _REENTRANT
1.14      thorpej    78: /* ..and a mutex to protect it all. */
                     79: static mutex_t atexit_mutex = MUTEX_INITIALIZER;
                     80: #endif /* _REENTRANT */
1.10      kleink     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.16.6.1! bouyer     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:
                    116: /*
                    117:  * Register an atexit routine.  This is suitable either for a cxa_atexit
                    118:  * or normal atexit type handler.  The __cxa_atexit() name and arguments
                    119:  * are specified by the C++ ABI.  See:
                    120:  *
                    121:  *     http://www.codesourcery.com/cxx-abi/abi.html#dso-dtor
1.1       cgd       122:  */
                    123: int
1.14      thorpej   124: __cxa_atexit(void (*func)(void *), void *arg, void *dso)
1.1       cgd       125: {
1.14      thorpej   126:        struct atexit_handler *ah;
1.11      lukem     127:
1.14      thorpej   128:        _DIAGASSERT(func != NULL);
1.1       cgd       129:
1.14      thorpej   130:        mutex_lock(&atexit_mutex);
                    131:
                    132:        ah = atexit_handler_alloc(dso);
                    133:        if (ah == NULL) {
                    134:                mutex_unlock(&atexit_mutex);
                    135:                return (-1);
1.1       cgd       136:        }
1.14      thorpej   137:
                    138:        ah->ah_cxa_atexit = func;
                    139:        ah->ah_arg = arg;
                    140:        ah->ah_dso = dso;
                    141:
                    142:        ah->ah_next = atexit_handler_stack;
                    143:        atexit_handler_stack = ah;
                    144:
                    145:        mutex_unlock(&atexit_mutex);
1.1       cgd       146:        return (0);
1.14      thorpej   147: }
                    148:
                    149: /*
                    150:  * Run the list of atexit handlers.  If dso is NULL, run all of them,
                    151:  * otherwise run only those matching the specified dso.
1.15      thorpej   152:  *
                    153:  * Note that we can be recursively invoked; rtld cleanup is via an
                    154:  * atexit handler, and rtld cleanup invokes _fini() for DSOs, which
                    155:  * in turn invokes __cxa_finalize() for the DSO.
1.14      thorpej   156:  */
                    157: void
                    158: __cxa_finalize(void *dso)
                    159: {
1.15      thorpej   160:        static thr_t owner;
                    161:        static u_int call_depth;
1.14      thorpej   162:        struct atexit_handler *ah, *dead_handlers = NULL, **prevp;
1.15      thorpej   163:        void (*cxa_func)(void *);
                    164:        void (*atexit_func)(void);
                    165:
                    166:        /*
                    167:         * We implement our own recursive mutex here because we need
                    168:         * to keep track of the call depth anyway, and it saves us
                    169:         * having to dynamically initialize the mutex.
                    170:         */
                    171:        if (mutex_trylock(&atexit_mutex) == 0)
                    172:                owner = thr_self();
                    173:        else if (owner != thr_self()) {
                    174:                mutex_lock(&atexit_mutex);
                    175:                owner = thr_self();
                    176:        }
1.14      thorpej   177:
1.15      thorpej   178:        call_depth++;
1.14      thorpej   179:
1.15      thorpej   180:        /*
                    181:         * If we are at call depth 1 (which is usually the "do everything"
                    182:         * call from exit(3)), we go ahead and remove elements from the
                    183:         * list as we call them.  This will prevent any nested calls from
                    184:         * having to traverse elements we've already processed.  If we are
                    185:         * at call depth > 1, we simply mark elements we process as unused.
                    186:         * When the depth 1 caller sees those, it will simply unlink them
                    187:         * for us.
                    188:         */
1.16.6.1! bouyer    189: again:
1.14      thorpej   190:        for (prevp = &atexit_handler_stack; (ah = (*prevp)) != NULL;) {
1.15      thorpej   191:                if (dso == NULL || dso == ah->ah_dso || ah->ah_atexit == NULL) {
                    192:                        if (ah->ah_atexit != NULL) {
1.16.6.1! bouyer    193:                                void *p = atexit_handler_stack;
1.15      thorpej   194:                                if (ah->ah_dso != NULL) {
                    195:                                        cxa_func = ah->ah_cxa_atexit;
                    196:                                        ah->ah_cxa_atexit = NULL;
                    197:                                        (*cxa_func)(ah->ah_arg);
                    198:                                } else {
                    199:                                        atexit_func = ah->ah_atexit;
                    200:                                        ah->ah_atexit = NULL;
                    201:                                        (*atexit_func)();
                    202:                                }
1.16.6.1! bouyer    203:                                /* Restart if new atexit handler was added. */
        !           204:                                if (p != atexit_handler_stack)
        !           205:                                        goto again;
1.14      thorpej   206:                        }
1.15      thorpej   207:
                    208:                        if (call_depth == 1) {
                    209:                                *prevp = ah->ah_next;
1.16.6.1! bouyer    210:                                if (STATIC_HANDLER_P(ah))
        !           211:                                        ah->ah_next = NULL;
        !           212:                                else {
1.15      thorpej   213:                                        ah->ah_next = dead_handlers;
                    214:                                        dead_handlers = ah;
                    215:                                }
                    216:                        } else
                    217:                                prevp = &ah->ah_next;
1.14      thorpej   218:                } else
                    219:                        prevp = &ah->ah_next;
                    220:        }
1.15      thorpej   221:
1.16      nathanw   222:        call_depth--;
                    223:
                    224:        if (call_depth > 0)
1.15      thorpej   225:                return;
1.14      thorpej   226:
                    227:        mutex_unlock(&atexit_mutex);
                    228:
                    229:        /*
                    230:         * Now free any dead handlers.  Do this even if we're about to
                    231:         * exit, in case a leak-detecting malloc is being used.
                    232:         */
                    233:        while ((ah = dead_handlers) != NULL) {
                    234:                dead_handlers = ah->ah_next;
                    235:                free(ah);
                    236:        }
                    237: }
                    238:
                    239: /*
                    240:  * Register a function to be performed at exit.
                    241:  */
                    242: int
                    243: atexit(void (*func)(void))
                    244: {
                    245:
                    246:        return (__cxa_atexit((void (*)(void *))func, NULL, NULL));
1.1       cgd       247: }

CVSweb <webmaster@jp.NetBSD.org>