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>