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