diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/librthread/Makefile | 4 | ||||
-rw-r--r-- | lib/librthread/rthread.c | 41 | ||||
-rw-r--r-- | lib/librthread/rthread.h | 7 | ||||
-rw-r--r-- | lib/librthread/rthread_fork.c | 177 |
4 files changed, 208 insertions, 21 deletions
diff --git a/lib/librthread/Makefile b/lib/librthread/Makefile index 1b325c8b8fb..958236ad04c 100644 --- a/lib/librthread/Makefile +++ b/lib/librthread/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.9 2006/01/05 04:06:48 marc Exp $ +# $OpenBSD: Makefile,v 1.10 2008/06/05 21:06:11 kurt Exp $ LIB=rthread WANTLINT= @@ -12,7 +12,7 @@ CFLAGS+=-I${LIBCSRCDIR}/include .PATH: ${.CURDIR}/arch/${MACHINE_ARCH} SRCS= rthread.c rthread_attr.c rthread_sched.c rthread_sync.c rthread_tls.c \ rthread_sig.c rthread_np.c rthread_debug.c rthread_stack.c \ - rthread_reaper.c rthread_libc.c + rthread_reaper.c rthread_libc.c rthread_fork.c OBJS+= _atomic_lock.o rfork_thread.o diff --git a/lib/librthread/rthread.c b/lib/librthread/rthread.c index 48e7da7691c..5e8a88de030 100644 --- a/lib/librthread/rthread.c +++ b/lib/librthread/rthread.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rthread.c,v 1.34 2007/05/18 14:36:17 art Exp $ */ +/* $OpenBSD: rthread.c,v 1.35 2008/06/05 21:06:11 kurt Exp $ */ /* * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> * All Rights Reserved. @@ -27,6 +27,7 @@ #include <machine/spinlock.h> +#include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> @@ -50,11 +51,6 @@ struct pthread _initial_thread; int rfork_thread(int, void *, void (*)(void *), void *); -#if defined(__ELF__) && defined(PIC) -static void rthread_dl_lock(int what); -static void rthread_bind_lock(int what); -#endif - /* * internal support functions */ @@ -100,6 +96,16 @@ _rthread_start(void *v) pthread_exit(retval); } +int +_rthread_open_kqueue(void) +{ + _rthread_kq = kqueue(); + if (_rthread_kq == -1) + return 1; + fcntl(_rthread_kq, F_SETFD, FD_CLOEXEC); + return 0; +} + static int _rthread_init(void) { @@ -112,8 +118,7 @@ _rthread_init(void) thread->flags_lock = _SPINLOCK_UNLOCKED; strlcpy(thread->name, "Main process", sizeof(thread->name)); LIST_INSERT_HEAD(&_thread_list, thread, threads); - _rthread_kq = kqueue(); - if (_rthread_kq == -1) + if (_rthread_open_kqueue()) return (errno); _rthread_debug_init(); _rthread_debug(1, "rthread init\n"); @@ -126,12 +131,12 @@ _rthread_init(void) * functions once to fully bind them before registering them * for use. */ - rthread_dl_lock(0); - rthread_dl_lock(1); - rthread_bind_lock(0); - rthread_bind_lock(1); - dlctl(NULL, DL_SETTHREADLCK, rthread_dl_lock); - dlctl(NULL, DL_SETBINDLCK, rthread_bind_lock); + _rthread_dl_lock(0); + _rthread_dl_lock(1); + _rthread_bind_lock(0); + _rthread_bind_lock(1); + dlctl(NULL, DL_SETTHREADLCK, _rthread_dl_lock); + dlctl(NULL, DL_SETBINDLCK, _rthread_bind_lock); #endif return (0); @@ -470,8 +475,8 @@ _thread_dump_info(void) } #if defined(__ELF__) && defined(PIC) -static void -rthread_dl_lock(int what) +void +_rthread_dl_lock(int what) { static _spinlock_lock_t lock = _SPINLOCK_UNLOCKED; @@ -481,8 +486,8 @@ rthread_dl_lock(int what) _spinunlock(&lock); } -static void -rthread_bind_lock(int what) +void +_rthread_bind_lock(int what) { static _spinlock_lock_t lock = _SPINLOCK_UNLOCKED; diff --git a/lib/librthread/rthread.h b/lib/librthread/rthread.h index 918a00d9e1c..1281b0dcece 100644 --- a/lib/librthread/rthread.h +++ b/lib/librthread/rthread.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rthread.h,v 1.17 2006/01/05 04:06:48 marc Exp $ */ +/* $OpenBSD: rthread.h,v 1.18 2008/06/05 21:06:11 kurt Exp $ */ /* * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> * All Rights Reserved. @@ -150,6 +150,11 @@ void _rthread_debug(int, const char *, ...) void _rthread_debug_init(void); void _rthread_add_to_reaper(pid_t, struct stack *); void _rthread_reaper(void); +int _rthread_open_kqueue(void); +#if defined(__ELF__) && defined(PIC) +void _rthread_dl_lock(int what); +void _rthread_bind_lock(int); +#endif void _thread_dump_info(void); diff --git a/lib/librthread/rthread_fork.c b/lib/librthread/rthread_fork.c new file mode 100644 index 00000000000..530d9ad5b6e --- /dev/null +++ b/lib/librthread/rthread_fork.c @@ -0,0 +1,177 @@ +/* $OpenBSD: rthread_fork.c,v 1.1 2008/06/05 21:06:11 kurt Exp $ */ + +/* + * Copyright (c) 2008 Kurt Miller <kurt@openbsd.org> + * Copyright (c) 2008 Philip Guenther <guenther@gmail.com> + * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: /repoman/r/ncvs/src/lib/libc_r/uthread/uthread_atfork.c,v 1.1 2004/12/10 03:36:45 grog Exp $ + */ + +#include <sys/param.h> + +#include <machine/spinlock.h> + +#include <errno.h> +#include <pthread.h> +#include <stdlib.h> +#include <unistd.h> + +#include "thread_private.h" /* in libc/include */ + +#include "rthread.h" + +struct rthread_atfork { + TAILQ_ENTRY(rthread_atfork) next; + void (*prepare)(void); + void (*parent)(void); + void (*child)(void); +}; + +static TAILQ_HEAD(atfork_listhead, rthread_atfork) _atfork_list = + TAILQ_HEAD_INITIALIZER(_atfork_list); + +static _spinlock_lock_t _atfork_lock = _SPINLOCK_UNLOCKED; + +pid_t _thread_sys_fork(void); +pid_t _thread_sys_vfork(void); +pid_t _dofork(int); + +pid_t +_dofork(int is_vfork) +{ + pthread_t me; + pid_t (*sys_fork)(void); + pid_t newid; + + sys_fork = is_vfork ? &_thread_sys_vfork : &_thread_sys_fork; + + if (!_threads_ready) + return sys_fork(); + + me = pthread_self(); + + /* + * Protect important libc/ld.so critical areas across the fork call. + * dlclose() will grab the atexit lock via __cxa_finalize() so lock + * the dl_lock first. malloc()/free() can grab the arc4 lock so lock + * malloc_lock first. Finally lock the bind_lock last so that any lazy + * binding in the other locking functions can succeed. + */ + +#if defined(__ELF__) && defined(PIC) + _rthread_dl_lock(0); +#endif + + _thread_atexit_lock(); + _thread_malloc_lock(); + _thread_arc4_lock(); + +#if defined(__ELF__) && defined(PIC) + _rthread_bind_lock(0); +#endif + + newid = sys_fork(); + +#if defined(__ELF__) && defined(PIC) + _rthread_bind_lock(1); +#endif + + _thread_arc4_unlock(); + _thread_malloc_unlock(); + _thread_atexit_unlock(); + +#if defined(__ELF__) && defined(PIC) + _rthread_dl_lock(1); +#endif + + if (newid == 0) { + if (_rthread_open_kqueue()) + _exit(126); /* XXX */ + + /* update this thread's structure */ + me->tid = getthrid(); + me->donesem.lock = _SPINLOCK_UNLOCKED; + me->flags &= ~THREAD_DETACHED; + me->flags_lock = _SPINLOCK_UNLOCKED; + + /* this thread is the initial thread for the new process */ + _initial_thread = *me; + + /* reinit the thread list */ + LIST_INIT(&_thread_list); + LIST_INSERT_HEAD(&_thread_list, &_initial_thread, threads); + _thread_lock = _SPINLOCK_UNLOCKED; + } + return newid; +} + +pid_t +fork(void) +{ + struct rthread_atfork *p; + pid_t newid; + + _spinlock(&_atfork_lock); + TAILQ_FOREACH_REVERSE(p, &_atfork_list, atfork_listhead, next) + if (p->prepare) + p->prepare(); + newid = _dofork(0); + if (newid == 0) { + TAILQ_FOREACH(p, &_atfork_list, next) + if (p->child) + p->child(); + } else { + TAILQ_FOREACH(p, &_atfork_list, next) + if (p->parent) + p->parent(); + } + _spinunlock(&_atfork_lock); + return newid; +} + +pid_t +vfork(void) +{ + return _dofork(1); +} + +int +pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)) +{ + struct rthread_atfork *af; + + if ((af = malloc(sizeof *af)) == NULL) + return (ENOMEM); + + af->prepare = prepare; + af->parent = parent; + af->child = child; + _spinlock(&_atfork_lock); + TAILQ_INSERT_TAIL(&_atfork_list, af, next); + _spinunlock(&_atfork_lock); + return (0); +} |