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>