rthread_fork.c revision 1.3
1/* $OpenBSD: rthread_fork.c,v 1.3 2009/11/27 19:42:24 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 46struct rthread_atfork { 47 TAILQ_ENTRY(rthread_atfork) next; 48 void (*prepare)(void); 49 void (*parent)(void); 50 void (*child)(void); 51}; 52 53static TAILQ_HEAD(atfork_listhead, rthread_atfork) _atfork_list = 54 TAILQ_HEAD_INITIALIZER(_atfork_list); 55 56static _spinlock_lock_t _atfork_lock = _SPINLOCK_UNLOCKED; 57 58pid_t _thread_sys_fork(void); 59pid_t _thread_sys_vfork(void); 60pid_t _dofork(int); 61 62pid_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 /* update this thread's structure */ 112 me->tid = getthrid(); 113 me->donesem.lock = _SPINLOCK_UNLOCKED; 114 me->flags &= ~THREAD_DETACHED; 115 me->flags_lock = _SPINLOCK_UNLOCKED; 116 117 /* this thread is the initial thread for the new process */ 118 _initial_thread = *me; 119 120 /* reinit the thread list */ 121 LIST_INIT(&_thread_list); 122 LIST_INSERT_HEAD(&_thread_list, &_initial_thread, threads); 123 _thread_lock = _SPINLOCK_UNLOCKED; 124 125 /* single threaded now */ 126 __isthreaded = 0; 127 } 128 return newid; 129} 130 131pid_t 132fork(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 155pid_t 156vfork(void) 157{ 158 return _dofork(1); 159} 160 161int 162pthread_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