nrelease - fix/improve livecd
[dragonfly.git] / lib / libthread_xu / thread / thr_fork.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3 * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Neither the name of the author nor the names of any co-contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/lib/libthr/thread/thr_fork.c 213096 2010-08-23 $
28 */
29
30/*
31 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
32 * All rights reserved.
33 *
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
36 * are met:
37 * 1. Redistributions of source code must retain the above copyright
38 * notice, this list of conditions and the following disclaimer.
39 * 2. Redistributions in binary form must reproduce the above copyright
40 * notice, this list of conditions and the following disclaimer in the
41 * documentation and/or other materials provided with the distribution.
42 * 3. Neither the name of the author nor the names of any co-contributors
43 * may be used to endorse or promote products derived from this software
44 * without specific prior written permission.
45 *
46 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
47 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
48 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
49 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
50 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
51 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
52 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
54 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
55 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 * SUCH DAMAGE.
57 *
58 */
59
60#include <sys/syscall.h>
61#include "namespace.h"
62#include <machine/tls.h>
63#include <errno.h>
64#include <link.h>
65#include <string.h>
66#include <stdlib.h>
67#include <unistd.h>
68#include <fcntl.h>
69#include <pthread.h>
70#include <spinlock.h>
71#include <sys/file.h>
72#include "un-namespace.h"
73
74#include "libc_private.h"
75#include "thr_private.h"
76
77struct atfork_head _thr_atfork_list;
78struct atfork_head _thr_atfork_kern_list;
79umtx_t _thr_atfork_lock;
80
81/*
82 * Execute a function in parent before and after the fork, and
83 * in the child.
84 */
85int
86_pthread_atfork(void (*prepare)(void), void (*parent)(void),
87 void (*child)(void))
88{
89 pthread_t curthread;
90 struct pthread_atfork *af;
91
92 af = __malloc(sizeof(struct pthread_atfork));
93 if (af == NULL)
94 return (ENOMEM);
95
96 curthread = tls_get_curthread();
97 af->prepare = prepare;
98 af->parent = parent;
99 af->child = child;
100 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
101 TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe);
102 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
103 return (0);
104}
105
106/*
107 * Private at-fork used by the rtld and sem code, guaranteed to order
108 * after user fork handlers in prepare, and before user fork handlers
109 * in the post-fork parent and in the child.
110 *
111 * This is used to ensure no interference between internal and user
112 * fork handlers, in particular we do not want to lock-out rtld or
113 * semaphores before user fork handlers run, and we want to recover
114 * before any user post-fork handlers run.
115 */
116void
117_thr_atfork_kern(void (*prepare)(void), void (*parent)(void),
118 void (*child)(void))
119{
120 pthread_t curthread;
121 struct pthread_atfork *af;
122
123 af = __malloc(sizeof(struct pthread_atfork));
124
125 curthread = tls_get_curthread();
126 af->prepare = prepare;
127 af->parent = parent;
128 af->child = child;
129 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
130 TAILQ_INSERT_TAIL(&_thr_atfork_kern_list, af, qe);
131 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
132}
133
134void
135__pthread_cxa_finalize(struct dl_phdr_info *phdr_info)
136{
137 pthread_t curthread;
138 struct pthread_atfork *af, *af1;
139
140 curthread = tls_get_curthread();
141 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
142 TAILQ_FOREACH_MUTABLE(af, &_thr_atfork_list, qe, af1) {
143 if (__elf_phdr_match_addr(phdr_info, af->prepare) ||
144 __elf_phdr_match_addr(phdr_info, af->parent) ||
145 __elf_phdr_match_addr(phdr_info, af->child)) {
146 TAILQ_REMOVE(&_thr_atfork_list, af, qe);
147 __free(af);
148 }
149 }
150 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
151}
152
153/*
154 * For a while, allow libpthread to work with a libc that doesn't
155 * export the malloc lock.
156 */
157#pragma weak __malloc_lock
158
159pid_t _fork(void);
160
161pid_t
162_fork(void)
163{
164 static umtx_t inprogress;
165 static int waiters;
166 umtx_t tmp;
167
168 pthread_t curthread;
169 struct pthread_atfork *af;
170 pid_t ret;
171 int errsave;
172#ifndef __DragonFly__
173 int unlock_malloc;
174#endif
175
176 if (!_thr_is_inited())
177 return (__syscall(SYS_fork));
178
179 errsave = errno;
180 curthread = tls_get_curthread();
181
182 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
183 tmp = inprogress;
184 while (tmp) {
185 waiters++;
186 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
187 _thr_umtx_wait(&inprogress, tmp, NULL, 0);
188 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
189 waiters--;
190 tmp = inprogress;
191 }
192 inprogress = 1;
193 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
194
195 /* Run down atfork prepare handlers. */
196 TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) {
197 if (af->prepare != NULL)
198 af->prepare();
199 }
200
201#ifndef __DragonFly__
202 /*
203 * Try our best to protect memory from being corrupted in
204 * child process because another thread in malloc code will
205 * simply be kill by fork().
206 */
207 if ((_thr_isthreaded() != 0) && (__malloc_lock != NULL)) {
208 unlock_malloc = 1;
209 _spinlock(__malloc_lock);
210 } else {
211 unlock_malloc = 0;
212 }
213#endif
214
215#ifdef _PTHREADS_DEBUGGING
216 _thr_log("fork-parent\n", 12);
217#endif
218
219 _thr_signal_block(curthread);
220
221 /*
222 * Must be executed Just before the fork.
223 */
224 TAILQ_FOREACH_REVERSE(af, &_thr_atfork_kern_list, atfork_head, qe) {
225 if (af->prepare != NULL)
226 af->prepare();
227 }
228
229 /* Fork a new process: */
230 if ((ret = __syscall(SYS_fork)) == 0) {
231 /*
232 * Child process.
233 *
234 * NOTE: We are using the saved errno from above. Do not
235 * reload errno here.
236 */
237 inprogress = 0;
238
239 /*
240 * Internal child fork handlers must be run immediately.
241 */
242 TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
243 if (af->child != NULL)
244 af->child();
245 }
246
247 curthread->cancelflags &= ~THR_CANCEL_NEEDED;
248 /*
249 * Thread list will be reinitialized, and later we call
250 * _libpthread_init(), it will add us back to list.
251 */
252 curthread->tlflags &= ~(TLFLAGS_IN_TDLIST | TLFLAGS_DETACHED);
253
254 /* child is a new kernel thread. */
255 curthread->tid = _thr_get_tid();
256
257 /* clear other threads locked us. */
258 _thr_umtx_init(&curthread->lock);
259 _thr_umtx_init(&_thr_atfork_lock);
260 _thr_setthreaded(0);
261
262 /* reinitialize libc spinlocks, this includes __malloc_lock. */
263 _thr_spinlock_init();
264#ifdef _PTHREADS_DEBUGGING
265 _thr_log("fork-child\n", 11);
266#endif
267 _mutex_fork(curthread, curthread->tid);
268
269 /* reinitalize library. */
270 _libpthread_init(curthread);
271
272 /* Ready to continue, unblock signals. */
273 _thr_signal_unblock(curthread);
274
275 /* Run down atfork child handlers. */
276 TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
277 if (af->child != NULL)
278 af->child();
279 }
280 } else {
281 /* Parent process */
282 errsave = errno;
283
284#ifndef __DragonFly__
285 if (unlock_malloc)
286 _spinunlock(__malloc_lock);
287#endif
288#ifdef _PTHREADS_DEBUGGING
289 _thr_log("fork-done\n", 10);
290#endif
291 /* Run down atfork parent handlers. */
292 TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
293 if (af->parent != NULL)
294 af->parent();
295 }
296
297 /* Ready to continue, unblock signals. */
298 _thr_signal_unblock(curthread);
299
300 TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
301 if (af->parent != NULL)
302 af->parent();
303 }
304
305 THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
306 inprogress = 0;
307 if (waiters)
308 _thr_umtx_wake(&inprogress, 0);
309 THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
310 }
311 errno = errsave;
312
313 /* Return the process ID: */
314 return (ret);
315}
316
317__strong_reference(_fork, fork);
318__strong_reference(_pthread_atfork, pthread_atfork);