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