1 /* $OpenBSD: rthread_fork.c,v 1.1 2008/06/05 21:06:11 kurt 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 return newid; 129 } 130 131 pid_t 132 fork(void) 133 { 134 struct rthread_atfork *p; 135 pid_t newid; 136 137 _spinlock(&_atfork_lock); 138 TAILQ_FOREACH_REVERSE(p, &_atfork_list, atfork_listhead, next) 139 if (p->prepare) 140 p->prepare(); 141 newid = _dofork(0); 142 if (newid == 0) { 143 TAILQ_FOREACH(p, &_atfork_list, next) 144 if (p->child) 145 p->child(); 146 } else { 147 TAILQ_FOREACH(p, &_atfork_list, next) 148 if (p->parent) 149 p->parent(); 150 } 151 _spinunlock(&_atfork_lock); 152 return newid; 153 } 154 155 pid_t 156 vfork(void) 157 { 158 return _dofork(1); 159 } 160 161 int 162 pthread_atfork(void (*prepare)(void), void (*parent)(void), 163 void (*child)(void)) 164 { 165 struct rthread_atfork *af; 166 167 if ((af = malloc(sizeof *af)) == NULL) 168 return (ENOMEM); 169 170 af->prepare = prepare; 171 af->parent = parent; 172 af->child = child; 173 _spinlock(&_atfork_lock); 174 TAILQ_INSERT_TAIL(&_atfork_list, af, next); 175 _spinunlock(&_atfork_lock); 176 return (0); 177 } 178