xref: /haiku/src/system/libroot/posix/unistd/fork.c (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
1 /*
2  * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <syscalls.h>
8 #include <fork.h>
9 
10 #include <unistd.h>
11 #include <stdlib.h>
12 #include <errno.h>
13 
14 
15 typedef struct fork_hook {
16 	struct fork_hook *next;
17 	void (*function)(void);
18 } fork_hook;
19 
20 static fork_hook *sPrepareHooks, *sParentHooks, *sChildHooks;
21 static fork_hook *sLastParentHook, *sLastChildHook;
22 static sem_id sForkLock;
23 
24 extern thread_id __main_thread_id;
25 
26 
27 /**	Adds a hook to the specified list.
28  *	If \a _lastHook is NULL, the hook will be added at the head of the list,
29  *	else it will be added at the tail of the list.
30  *	Since this function allocates memory, it can fail, and returns B_NO_MEMORY
31  *	in that case. It returns B_OK on success.
32  */
33 
34 static status_t
35 add_fork_hook(fork_hook **_hooks, fork_hook **_lastHook, void (*function)(void))
36 {
37 	fork_hook *hook = (fork_hook *)malloc(sizeof(struct fork_hook));
38 	if (hook == NULL)
39 		return B_NO_MEMORY;
40 
41 	hook->function = function;
42 
43 	if (_lastHook) {
44 		// add hook at the end of the list
45 
46 		if (*_hooks == NULL) {
47 			// first entry of this list
48 			*_hooks = hook;
49 			*_lastHook = hook;
50 		} else {
51 			// any other item
52 #if 0
53 			if (*_lastHook == NULL) {
54 				// search for last hook (need if an item was added to the beginning only --
55 				// this can only be the case if this function is called directly, though)
56 				fork_hook *last = *_hooks;
57 				while (last->next)
58 					last = last->next;
59 
60 				*_lastHook = last;
61 			}
62 #endif
63 
64 			(*_lastHook)->next = hook;
65 			*_lastHook = hook;
66 		}
67 
68 		hook->next = NULL;
69 	} else {
70 		// add hook at the beginning of the list
71 		hook->next = *_hooks;
72 		*_hooks = hook;
73 	}
74 
75 	return B_OK;
76 }
77 
78 
79 /**	Calls all hooks in the specified list in ascending order.
80  */
81 
82 static void
83 call_fork_hooks(fork_hook *hook)
84 {
85 	while (hook) {
86 		hook->function();
87 		hook = hook->next;
88 	}
89 }
90 
91 
92 status_t
93 __init_fork(void)
94 {
95 	sForkLock = create_sem(1, "fork lock");
96 	if (sForkLock < B_OK)
97 		return sForkLock;
98 
99 	return B_OK;
100 }
101 
102 
103 /**	Private support function that registers the hooks that will be executed
104  *	before and after the team is fork()ed.
105  *	It is called from pthread_atfork() and atfork().
106  */
107 
108 status_t
109 __register_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
110 {
111 	status_t status;
112 
113 	while ((status = acquire_sem(sForkLock)) == B_INTERRUPTED);
114 	if (status != B_OK)
115 		return status;
116 
117 	if (prepare)
118 		status = add_fork_hook(&sPrepareHooks, NULL, prepare);
119 
120 	if (status == B_OK && parent)
121 		status = add_fork_hook(&sParentHooks, &sLastParentHook, parent);
122 
123 	if (status == B_OK && child)
124 		status = add_fork_hook(&sChildHooks, &sLastChildHook, child);
125 
126 	release_sem(sForkLock);
127 	return status;
128 }
129 
130 
131 pid_t
132 fork(void)
133 {
134 	thread_id thread;
135 	status_t status;
136 
137 	while ((status = acquire_sem(sForkLock)) == B_INTERRUPTED);
138 	if (status != B_OK)
139 		return status;
140 
141 	// call preparation hooks
142 	call_fork_hooks(sPrepareHooks);
143 
144 	thread = _kern_fork();
145 	if (thread < 0) {
146 		// something went wrong
147 		errno = thread;
148 		return -1;
149 	}
150 
151 	if (thread == 0) {
152 		// we are the child
153 		// ToDo: initialize child
154 		__main_thread_id = find_thread(NULL);
155 		__init_fork();
156 
157 		call_fork_hooks(sChildHooks);
158 	} else {
159 		// we are the parent
160 		call_fork_hooks(sParentHooks);
161 		release_sem(sForkLock);
162 	}
163 
164 	return thread;
165 }
166 
167