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