1 /* $OpenBSD: rthread_fork.c,v 1.2 2009/10/21 15:32:01 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Kurt Miller <kurt@openbsd.org> 5 * Copyright (c) 2008 Philip Guenther <guenther@gmail.com> 6 * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Neither the name of the author nor the names of any co-contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD: /repoman/r/ncvs/src/lib/libc_r/uthread/uthread_atfork.c,v 1.1 2004/12/10 03:36:45 grog Exp $ 31 */ 32 33 #include <sys/param.h> 34 35 #include <machine/spinlock.h> 36 37 #include <errno.h> 38 #include <pthread.h> 39 #include <stdlib.h> 40 #include <unistd.h> 41 42 #include "thread_private.h" /* in libc/include */ 43 44 #include "rthread.h" 45 46 struct rthread_atfork { 47 TAILQ_ENTRY(rthread_atfork) next; 48 void (*prepare)(void); 49 void (*parent)(void); 50 void (*child)(void); 51 }; 52 53 static TAILQ_HEAD(atfork_listhead, rthread_atfork) _atfork_list = 54 TAILQ_HEAD_INITIALIZER(_atfork_list); 55 56 static _spinlock_lock_t _atfork_lock = _SPINLOCK_UNLOCKED; 57 58 pid_t _thread_sys_fork(void); 59 pid_t _thread_sys_vfork(void); 60 pid_t _dofork(int); 61 62 pid_t 63 _dofork(int is_vfork) 64 { 65 pthread_t me; 66 pid_t (*sys_fork)(void); 67 pid_t newid; 68 69 sys_fork = is_vfork ? &_thread_sys_vfork : &_thread_sys_fork; 70 71 if (!_threads_ready) 72 return sys_fork(); 73 74 me = pthread_self(); 75 76 /* 77 * Protect important libc/ld.so critical areas across the fork call. 78 * dlclose() will grab the atexit lock via __cxa_finalize() so lock 79 * the dl_lock first. malloc()/free() can grab the arc4 lock so lock 80 * malloc_lock first. Finally lock the bind_lock last so that any lazy 81 * binding in the other locking functions can succeed. 82 */ 83 84 #if defined(__ELF__) && defined(PIC) 85 _rthread_dl_lock(0); 86 #endif 87 88 _thread_atexit_lock(); 89 _thread_malloc_lock(); 90 _thread_arc4_lock(); 91 92 #if defined(__ELF__) && defined(PIC) 93 _rthread_bind_lock(0); 94 #endif 95 96 newid = sys_fork(); 97 98 #if defined(__ELF__) && defined(PIC) 99 _rthread_bind_lock(1); 100 #endif 101 102 _thread_arc4_unlock(); 103 _thread_malloc_unlock(); 104 _thread_atexit_unlock(); 105 106 #if defined(__ELF__) && defined(PIC) 107 _rthread_dl_lock(1); 108 #endif 109 110 if (newid == 0) { 111 if (_rthread_open_kqueue()) 112 _exit(126); /* XXX */ 113 114 /* update this thread's structure */ 115 me->tid = getthrid(); 116 me->donesem.lock = _SPINLOCK_UNLOCKED; 117 me->flags &= ~THREAD_DETACHED; 118 me->flags_lock = _SPINLOCK_UNLOCKED; 119 120 /* this thread is the initial thread for the new process */ 121 _initial_thread = *me; 122 123 /* reinit the thread list */ 124 LIST_INIT(&_thread_list); 125 LIST_INSERT_HEAD(&_thread_list, &_initial_thread, threads); 126 _thread_lock = _SPINLOCK_UNLOCKED; 127 128 /* single threaded now */ 129 __isthreaded = 0; 130 } 131 return newid; 132 } 133 134 pid_t 135 fork(void) 136 { 137 struct rthread_atfork *p; 138 pid_t newid; 139 140 _spinlock(&_atfork_lock); 141 TAILQ_FOREACH_REVERSE(p, &_atfork_list, atfork_listhead, next) 142 if (p->prepare) 143 p->prepare(); 144 newid = _dofork(0); 145 if (newid == 0) { 146 TAILQ_FOREACH(p, &_atfork_list, next) 147 if (p->child) 148 p->child(); 149 } else { 150 TAILQ_FOREACH(p, &_atfork_list, next) 151 if (p->parent) 152 p->parent(); 153 } 154 _spinunlock(&_atfork_lock); 155 return newid; 156 } 157 158 pid_t 159 vfork(void) 160 { 161 return _dofork(1); 162 } 163 164 int 165 pthread_atfork(void (*prepare)(void), void (*parent)(void), 166 void (*child)(void)) 167 { 168 struct rthread_atfork *af; 169 170 if ((af = malloc(sizeof *af)) == NULL) 171 return (ENOMEM); 172 173 af->prepare = prepare; 174 af->parent = parent; 175 af->child = child; 176 _spinlock(&_atfork_lock); 177 TAILQ_INSERT_TAIL(&_atfork_list, af, next); 178 _spinunlock(&_atfork_lock); 179 return (0); 180 } 181