xref: /haiku/src/system/libroot/posix/stdlib/exit.cpp (revision dfc8a217db488098641462dfc334dcc0f7d62456)
1 /*
2  * Copyright 2004-2010, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Author(s):
6  *		Daniel Reinhold, danielre@users.sf.net
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Ingo Weinhold, ingo_weinhold@gmx.de
9  */
10 
11 
12 #include <SupportDefs.h>
13 
14 #include <limits.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 
19 #include <new>
20 
21 #include <util/DoublyLinkedList.h>
22 #include <util/SinglyLinkedList.h>
23 
24 #include <libroot_private.h>
25 #include <locks.h>
26 #include <runtime_loader.h>
27 #include <syscalls.h>
28 
29 
30 extern "C" void _IO_cleanup(void);
31 extern "C" void _thread_do_exit_work(void);
32 
33 
34 struct AtExitInfoBlock;
35 
36 struct AtExitInfo : SinglyLinkedListLinkImpl<AtExitInfo> {
37 	AtExitInfoBlock*	block;
38 	void				(*hook)(void*);
39 	void*				data;
40 	void*				dsoHandle;
41 };
42 
43 typedef SinglyLinkedList<AtExitInfo> AtExitInfoList;
44 
45 
46 struct AtExitInfoBlock : DoublyLinkedListLinkImpl<AtExitInfoBlock> {
47 	bool IsEmpty() const
48 	{
49 		return fFirstUnused == ATEXIT_MAX && fFreeList.IsEmpty();
50 	}
51 
52 	AtExitInfo* AllocateInfo()
53 	{
54 		// Handle the likely case -- the block is not fully used yet -- first.
55 		// Grab the next info from the array.
56 		if (fFirstUnused < ATEXIT_MAX) {
57 			AtExitInfo* info = &fInfos[fFirstUnused++];
58 			info->block = this;
59 			return info;
60 		}
61 
62 		// The block was fully used, but there might be infos in the free list.
63 		return fFreeList.RemoveHead();
64 	}
65 
66 	void FreeInfo(AtExitInfo* info)
67 	{
68 		fFreeList.Add(info);
69 	}
70 
71 private:
72 	AtExitInfo		fInfos[ATEXIT_MAX];
73 	uint32			fFirstUnused;
74 	AtExitInfoList	fFreeList;
75 };
76 
77 typedef DoublyLinkedList<AtExitInfoBlock> AtExitInfoBlockList;
78 
79 
80 struct DSOPredicate {
81 	DSOPredicate(void* dsoHandle)
82 		:
83 		fDSOHandle(dsoHandle)
84 	{
85 	}
86 
87 	inline bool operator()(const AtExitInfo* info) const
88 	{
89 		return info->dsoHandle == fDSOHandle;
90 	}
91 
92 private:
93 	void*	fDSOHandle;
94 };
95 
96 
97 struct AddressRangePredicate {
98 	AddressRangePredicate(addr_t start, size_t size)
99 		:
100 		fStart(start),
101 		fEnd(start + size - 1)
102 	{
103 	}
104 
105 	inline bool operator()(const AtExitInfo* info) const
106 	{
107 		addr_t address = (addr_t)info->hook;
108 		return info->dsoHandle == NULL && address >= fStart && address <= fEnd;
109 			// Note: We ignore hooks associated with an image (the same one
110 			// likely), since those will be called anyway when __cxa_finalize()
111 			// is invoked for that image.
112 	}
113 
114 private:
115 	addr_t	fStart;
116 	addr_t	fEnd;
117 };
118 
119 
120 static AtExitInfoBlock sInitialAtExistInfoBlock;
121 static AtExitInfoBlockList sAtExitInfoBlocks;
122 static AtExitInfoList sAtExitInfoStack;
123 static recursive_lock sAtExitLock = RECURSIVE_LOCK_INITIALIZER("at exit lock");
124 
125 
126 static void inline
127 _exit_stack_lock()
128 {
129 	recursive_lock_lock(&sAtExitLock);
130 }
131 
132 
133 static void inline
134 _exit_stack_unlock()
135 {
136 	recursive_lock_unlock(&sAtExitLock);
137 }
138 
139 
140 template<typename Predicate>
141 static void
142 call_exit_hooks(const Predicate& predicate)
143 {
144 	_exit_stack_lock();
145 
146 	AtExitInfo* previousInfo = NULL;
147 	AtExitInfo* info = sAtExitInfoStack.Head();
148 	while (info != NULL) {
149 		AtExitInfo* nextInfo = sAtExitInfoStack.GetNext(info);
150 
151 		if (predicate(info)) {
152 			// remove info from stack
153 			sAtExitInfoStack.Remove(previousInfo, info);
154 
155 			// call the hook
156 			info->hook(info->data);
157 
158 			// return the info to the block
159 			if (info->block->IsEmpty())
160 				sAtExitInfoBlocks.Add(info->block);
161 
162 			info->block->FreeInfo(info);
163 		} else
164 			previousInfo = info;
165 
166 		info = nextInfo;
167 	}
168 
169 	_exit_stack_unlock();
170 }
171 
172 
173 // #pragma mark -- C++ ABI
174 
175 
176 /*!	exit() hook registration function (mandated by the C++ ABI).
177 	\param hook Hook function to be called.
178 	\param data The data to be passed to the hook.
179 	\param dsoHandle If non-NULL, the hook is associated with the respective
180 		loaded shared object (aka image) -- the hook will be called either on
181 		exit() or earlier when the shared object is unloaded. If NULL, the hook
182 		is called only on exit().
183 	\return \c 0 on success, another value on failure.
184  */
185 extern "C" int
186 __cxa_atexit(void (*hook)(void*), void* data, void* dsoHandle)
187 {
188 	if (hook == NULL)
189 		return -1;
190 
191 	_exit_stack_lock();
192 
193 	// We need to allocate an info. Get an info block from which to allocate.
194 	AtExitInfoBlock* block = sAtExitInfoBlocks.Head();
195 	if (block == NULL) {
196 		// might be the first call -- check the initial block
197 		if (!sInitialAtExistInfoBlock.IsEmpty()) {
198 			block = &sInitialAtExistInfoBlock;
199 		} else {
200 			// no empty block -- let's hope libroot is initialized sufficiently
201 			// for the heap to work
202 			block = new(std::nothrow) AtExitInfoBlock;
203 			if (block == NULL) {
204 				_exit_stack_unlock();
205 				return -1;
206 			}
207 		}
208 
209 		sAtExitInfoBlocks.Add(block);
210 	}
211 
212 	// allocate the info
213 	AtExitInfo* info = block->AllocateInfo();
214 
215 	// If the block is empty now, remove it from the list.
216 	if (block->IsEmpty())
217 		sAtExitInfoBlocks.Remove(block);
218 
219 	// init and add the info
220 	info->hook = hook;
221 	info->data = data;
222 	info->dsoHandle = dsoHandle;
223 
224 	sAtExitInfoStack.Add(info);
225 
226 	_exit_stack_unlock();
227 
228 	return 0;
229 }
230 
231 
232 /*!	exit() hook calling function (mandated by the C++ ABI).
233 
234 	Calls the exit() hooks associated with a certain shared object handle,
235 	respectively calls all hooks when a NULL handle is given. All called
236 	hooks are removed.
237 
238 	\param dsoHandle If non-NULL, all hooks associated with that handle are
239 		called. If NULL, all hooks are called.
240  */
241 extern "C" void
242 __cxa_finalize(void* dsoHandle)
243 {
244 	if (dsoHandle == NULL) {
245 		// call all hooks
246 		_exit_stack_lock();
247 
248 		while (AtExitInfo* info = sAtExitInfoStack.RemoveHead()) {
249 			// call the hook
250 			info->hook(info->data);
251 
252 			// return the info to the block
253 			if (info->block->IsEmpty())
254 				sAtExitInfoBlocks.Add(info->block);
255 
256 			info->block->FreeInfo(info);
257 		}
258 
259 		_exit_stack_unlock();
260 	} else {
261 		// call all hooks for the respective DSO
262 		call_exit_hooks(DSOPredicate(dsoHandle));
263 	}
264 }
265 
266 
267 // #pragma mark - private API
268 
269 
270 void
271 _call_atexit_hooks_for_range(addr_t start, addr_t size)
272 {
273 	call_exit_hooks(AddressRangePredicate(start, size));
274 }
275 
276 
277 // #pragma mark - public API
278 
279 
280 void
281 abort()
282 {
283 	fprintf(stderr, "Abort\n");
284 
285 	raise(SIGABRT);
286 	exit(EXIT_FAILURE);
287 }
288 
289 
290 int
291 atexit(void (*func)(void))
292 {
293 	return __cxa_atexit((void (*)(void*))func, NULL, NULL);
294 }
295 
296 
297 void
298 exit(int status)
299 {
300 	// BeOS on exit notification for the main thread
301 	_thread_do_exit_work();
302 
303 	// unwind the exit stack, calling the registered functions
304 	__cxa_finalize(NULL);
305 
306 	// close all open files
307 	_IO_cleanup();
308 
309 	__gRuntimeLoader->call_termination_hooks();
310 
311 	// exit with status code
312 	_kern_exit_team(status);
313 }
314