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