Index: Makefile =================================================================== RCS file: /cvsroot/src/lib/libpthread/Makefile,v retrieving revision 1.56 diff -u -r1.56 Makefile --- Makefile 16 May 2009 22:21:18 -0000 1.56 +++ Makefile 15 Feb 2010 12:01:50 -0000 @@ -39,6 +39,7 @@ # library semantics will end up discarding potentially important objects. # SRCS= pthread.c +SRCS+= pthread_atfork.c SRCS+= pthread_attr.c SRCS+= pthread_barrier.c SRCS+= pthread_cancelstub.c Index: pthread.c =================================================================== RCS file: /cvsroot/src/lib/libpthread/pthread.c,v retrieving revision 1.113 diff -u -r1.113 pthread.c --- pthread.c 3 Oct 2009 23:49:50 -0000 1.113 +++ pthread.c 15 Feb 2010 12:01:50 -0000 @@ -68,10 +68,7 @@ static int pthread__stackid_setup(void *, size_t, pthread_t *); static int pthread__stackalloc(pthread_t *); static void pthread__initmain(pthread_t *); -static void pthread__fork_callback(void); static void pthread__reap(pthread_t); -static void pthread__child_callback(void); -static void pthread__start(void); void pthread__init(void); @@ -82,7 +79,7 @@ static pthread_attr_t pthread_default_attr; static lwpctl_t pthread__dummy_lwpctl = { .lc_curcpu = LWPCTL_CPU_NONE }; -static pthread_t pthread__first; +pthread_t pthread__first; enum { DIAGASSERT_ABORT = 1<<0, @@ -228,50 +225,9 @@ /* Tell libc that we're here and it should role-play accordingly. */ pthread__first = first; - pthread_atfork(NULL, NULL, pthread__fork_callback); __isthreaded = 1; } -static void -pthread__fork_callback(void) -{ - - /* lwpctl state is not copied across fork. */ - if (_lwp_ctl(LWPCTL_FEATURE_CURCPU, &pthread__first->pt_lwpctl)) { - err(1, "_lwp_ctl"); - } -} - -static void -pthread__child_callback(void) -{ - - /* - * Clean up data structures that a forked child process might - * trip over. Note that if threads have been created (causing - * this handler to be registered) the standards say that the - * child will trigger undefined behavior if it makes any - * pthread_* calls (or any other calls that aren't - * async-signal-safe), so we don't really have to clean up - * much. Anything that permits some pthread_* calls to work is - * merely being polite. - */ - pthread__started = 0; -} - -static void -pthread__start(void) -{ - - /* - * Per-process timers are cleared by fork(); despite the - * various restrictions on fork() and threads, it's legal to - * fork() before creating any threads. - */ - pthread_atfork(NULL, NULL, pthread__child_callback); -} - - /* General-purpose thread data structure sanitization. */ /* ARGSUSED */ static void @@ -327,10 +283,8 @@ * It's okay to check this without a lock because there can * only be one thread before it becomes true. */ - if (pthread__started == 0) { - pthread__start(); + if (pthread__started == 0) pthread__started = 1; - } if (attr == NULL) nattr = pthread_default_attr; Index: pthread_atfork.c =================================================================== RCS file: pthread_atfork.c diff -N pthread_atfork.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ pthread_atfork.c 15 Feb 2010 12:01:50 -0000 @@ -0,0 +1,167 @@ +/* $NetBSD$ */ + +/*- + * Copyright (c) 2002, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Nathan J. Williams, Takehiko NOZAKI. + * + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD$"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pthread.h" +#include "pthread_int.h" + +pid_t __fork(void); /* XXX */ + +struct atfork_callback { + TAILQ_ENTRY(atfork_callback) entry; + void (*preparefn)(void); + void (*parentfn)(void); + void (*childfn)(void); +}; + +/* + * Hypothetically, we could protect the queues with a rwlock which is + * write-locked by pthread_atfork() and read-locked by fork(), but + * since the intended use of the functions is obtaining locks to hold + * across the fork, forking is going to be serialized anyway. + */ +static pthread_mutex_t atfork_lock = PTHREAD_MUTEX_INITIALIZER; + +TAILQ_HEAD(atfork_callback_q, atfork_callback); +static struct atfork_callback_q atforkq = TAILQ_HEAD_INITIALIZER(atforkq); + +int +pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)) +{ + struct atfork_callback *newatfork; + + if (prepare != NULL || parent != NULL || child != NULL) { + newatfork = malloc(sizeof(struct atfork_callback)); + if (newatfork == NULL) + return ENOMEM; + newatfork->preparefn = prepare; + newatfork->parentfn = parent; + newatfork->childfn = child; + pthread_mutex_lock(&atfork_lock); + TAILQ_INSERT_TAIL(&atforkq, newatfork, entry); + pthread_mutex_unlock(&atfork_lock); + } + + return 0; +} + +/* XXX: FIXME */ +extern int pthread__started; +extern pthread_t pthread__first; + +pid_t +fork(void) +{ + struct atfork_callback *head, *tail, *iter; + pid_t ret; + pthread_mutex_lock(&atfork_lock); + head = TAILQ_FIRST(&atforkq); + tail = TAILQ_LAST(&atforkq, atfork_callback_q); + pthread_mutex_unlock(&atfork_lock); + + /* + * The order in which the functions are called is specified as + * LIFO for the prepare handler and FIFO for the others. + */ + if (tail != NULL) { + for (iter = tail; /**/; + iter = TAILQ_PREV(iter, atfork_callback_q, entry)) { + if (iter->preparefn) + (*iter->preparefn)(); + if (iter == head) + break; + } + } + + ret = __fork(); + + if (ret != 0) { + /* + * We are the parent. It doesn't matter here whether + * the fork call succeeded or failed. + */ + if (head != NULL) { + for (iter = head; /**/; + iter = TAILQ_NEXT(iter, entry)) { + if (iter->parentfn) + (*iter->parentfn)(); + if (iter == tail) + break; + } + } + } else { + /* lwpctl state is not copied across fork. */ + if (_lwp_ctl(LWPCTL_FEATURE_CURCPU, &pthread__first->pt_lwpctl)) + err(1, "_lwp_ctl"); + + /* + * Clean up data structures that a forked child process might + * trip over. Note that if threads have been created (causing + * this handler to be registered) the standards say that the + * child will trigger undefined behavior if it makes any + * pthread_* calls (or any other calls that aren't + * async-signal-safe), so we don't really have to clean up + * much. Anything that permits some pthread_* calls to work is + * merely being polite. + */ + pthread__started = 0; + + /* We are the child */ + if (head != NULL) { + for (iter = head; /**/; + iter = TAILQ_NEXT(iter, entry)) { + if (iter->childfn) + (*iter->childfn)(); + if (iter == tail) + break; + } + } + } + + return ret; +}