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