xref: /dragonfly/lib/libthread_xu/thread/thr_fork.c (revision 940be950819fa932cd401a01f1182bf686a2e61e)
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 
77 struct atfork_head  _thr_atfork_list;
78 struct atfork_head  _thr_atfork_kern_list;
79 umtx_t    _thr_atfork_lock;
80 
81 /*
82  * Execute a function in parent before and after the fork, and
83  * in the child.
84  */
85 int
_pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))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  */
116 void
_thr_atfork_kern(void (* prepare)(void),void (* parent)(void),void (* child)(void))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 
134 void
__pthread_cxa_finalize(struct dl_phdr_info * phdr_info)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 
159 pid_t _fork(void);
160 
161 pid_t
_fork(void)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);
319