xref: /dflybsd-src/lib/libthread_xu/thread/thr_fork.c (revision 940be950819fa932cd401a01f1182bf686a2e61e)
171b3fa15SDavid Xu /*
271b3fa15SDavid Xu  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
371b3fa15SDavid Xu  * Copyright (c) 2003 Daniel Eischen <deischen@freebsd.org>
471b3fa15SDavid Xu  * All rights reserved.
571b3fa15SDavid Xu  *
671b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
771b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
871b3fa15SDavid Xu  * are met:
971b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
1071b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer.
1171b3fa15SDavid Xu  * 2. Neither the name of the author nor the names of any co-contributors
1271b3fa15SDavid Xu  *    may be used to endorse or promote products derived from this software
1371b3fa15SDavid Xu  *    without specific prior written permission.
1471b3fa15SDavid Xu  *
1571b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1671b3fa15SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1771b3fa15SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1871b3fa15SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1971b3fa15SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2071b3fa15SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2171b3fa15SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2271b3fa15SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2371b3fa15SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2471b3fa15SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2571b3fa15SDavid Xu  * SUCH DAMAGE.
2671b3fa15SDavid Xu  *
27fcf53d9bSJohn Marino  * $FreeBSD: head/lib/libthr/thread/thr_fork.c 213096 2010-08-23 $
2871b3fa15SDavid Xu  */
2971b3fa15SDavid Xu 
3071b3fa15SDavid Xu /*
3171b3fa15SDavid Xu  * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3271b3fa15SDavid Xu  * All rights reserved.
3371b3fa15SDavid Xu  *
3471b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
3571b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
3671b3fa15SDavid Xu  * are met:
3771b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
3871b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer.
3971b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
4071b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
4171b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
42fcf53d9bSJohn Marino  * 3. Neither the name of the author nor the names of any co-contributors
4371b3fa15SDavid Xu  *    may be used to endorse or promote products derived from this software
4471b3fa15SDavid Xu  *    without specific prior written permission.
4571b3fa15SDavid Xu  *
4671b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
4771b3fa15SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4871b3fa15SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4971b3fa15SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
5071b3fa15SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5171b3fa15SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5271b3fa15SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5371b3fa15SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5471b3fa15SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5571b3fa15SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5671b3fa15SDavid Xu  * SUCH DAMAGE.
5771b3fa15SDavid Xu  *
5871b3fa15SDavid Xu  */
599e2ee207SJoerg Sonnenberger 
6082657471SMarkus Pfeiffer #include <sys/syscall.h>
61f049c502Szrj #include "namespace.h"
629e2ee207SJoerg Sonnenberger #include <machine/tls.h>
6371b3fa15SDavid Xu #include <errno.h>
64fcf53d9bSJohn Marino #include <link.h>
6571b3fa15SDavid Xu #include <string.h>
6671b3fa15SDavid Xu #include <stdlib.h>
6771b3fa15SDavid Xu #include <unistd.h>
6871b3fa15SDavid Xu #include <fcntl.h>
6971b3fa15SDavid Xu #include <pthread.h>
7071b3fa15SDavid Xu #include <spinlock.h>
7198247283SMatthew Dillon #include <sys/file.h>
72fc71f871SDavid Xu #include "un-namespace.h"
7371b3fa15SDavid Xu 
7471b3fa15SDavid Xu #include "libc_private.h"
7571b3fa15SDavid Xu #include "thr_private.h"
7671b3fa15SDavid Xu 
77e8382b15SDavid Xu struct atfork_head	_thr_atfork_list;
78fcaa7a3aSMatthew Dillon struct atfork_head	_thr_atfork_kern_list;
79e8382b15SDavid Xu umtx_t	_thr_atfork_lock;
80e8382b15SDavid Xu 
81fcaa7a3aSMatthew Dillon /*
82fcaa7a3aSMatthew Dillon  * Execute a function in parent before and after the fork, and
83fcaa7a3aSMatthew Dillon  * in the child.
84fcaa7a3aSMatthew Dillon  */
8571b3fa15SDavid Xu int
_pthread_atfork(void (* prepare)(void),void (* parent)(void),void (* child)(void))8671b3fa15SDavid Xu _pthread_atfork(void (*prepare)(void), void (*parent)(void),
8771b3fa15SDavid Xu 		void (*child)(void))
8871b3fa15SDavid Xu {
89*940be950Szrj 	pthread_t curthread;
9071b3fa15SDavid Xu 	struct pthread_atfork *af;
9171b3fa15SDavid Xu 
92e7bf3f77SMatthew Dillon 	af = __malloc(sizeof(struct pthread_atfork));
93e7bf3f77SMatthew Dillon 	if (af == NULL)
9471b3fa15SDavid Xu 		return (ENOMEM);
9571b3fa15SDavid Xu 
969e2ee207SJoerg Sonnenberger 	curthread = tls_get_curthread();
9771b3fa15SDavid Xu 	af->prepare = prepare;
9871b3fa15SDavid Xu 	af->parent = parent;
9971b3fa15SDavid Xu 	af->child = child;
10071b3fa15SDavid Xu 	THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
10171b3fa15SDavid Xu 	TAILQ_INSERT_TAIL(&_thr_atfork_list, af, qe);
10271b3fa15SDavid Xu 	THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
10371b3fa15SDavid Xu 	return (0);
10471b3fa15SDavid Xu }
10571b3fa15SDavid Xu 
106fcaa7a3aSMatthew Dillon /*
107fcaa7a3aSMatthew Dillon  * Private at-fork used by the rtld and sem code, guaranteed to order
108fcaa7a3aSMatthew Dillon  * after user fork handlers in prepare, and before user fork handlers
109fcaa7a3aSMatthew Dillon  * in the post-fork parent and in the child.
110fcaa7a3aSMatthew Dillon  *
111fcaa7a3aSMatthew Dillon  * This is used to ensure no interference between internal and user
112fcaa7a3aSMatthew Dillon  * fork handlers, in particular we do not want to lock-out rtld or
113fcaa7a3aSMatthew Dillon  * semaphores before user fork handlers run, and we want to recover
114fcaa7a3aSMatthew Dillon  * before any user post-fork handlers run.
115fcaa7a3aSMatthew Dillon  */
116fcaa7a3aSMatthew Dillon void
_thr_atfork_kern(void (* prepare)(void),void (* parent)(void),void (* child)(void))117fcaa7a3aSMatthew Dillon _thr_atfork_kern(void (*prepare)(void), void (*parent)(void),
118fcaa7a3aSMatthew Dillon 		 void (*child)(void))
119fcaa7a3aSMatthew Dillon {
120*940be950Szrj 	pthread_t curthread;
121fcaa7a3aSMatthew Dillon 	struct pthread_atfork *af;
122fcaa7a3aSMatthew Dillon 
123e7bf3f77SMatthew Dillon 	af = __malloc(sizeof(struct pthread_atfork));
124fcaa7a3aSMatthew Dillon 
125fcaa7a3aSMatthew Dillon 	curthread = tls_get_curthread();
126fcaa7a3aSMatthew Dillon 	af->prepare = prepare;
127fcaa7a3aSMatthew Dillon 	af->parent = parent;
128fcaa7a3aSMatthew Dillon 	af->child = child;
129fcaa7a3aSMatthew Dillon 	THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
130fcaa7a3aSMatthew Dillon 	TAILQ_INSERT_TAIL(&_thr_atfork_kern_list, af, qe);
131fcaa7a3aSMatthew Dillon 	THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
132fcaa7a3aSMatthew Dillon }
133fcaa7a3aSMatthew Dillon 
134fcf53d9bSJohn Marino void
__pthread_cxa_finalize(struct dl_phdr_info * phdr_info)135fcf53d9bSJohn Marino __pthread_cxa_finalize(struct dl_phdr_info *phdr_info)
136fcf53d9bSJohn Marino {
137*940be950Szrj 	pthread_t curthread;
138fcf53d9bSJohn Marino 	struct pthread_atfork *af, *af1;
139fcf53d9bSJohn Marino 
140fcf53d9bSJohn Marino 	curthread = tls_get_curthread();
141fcf53d9bSJohn Marino 	THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
142fcf53d9bSJohn Marino 	TAILQ_FOREACH_MUTABLE(af, &_thr_atfork_list, qe, af1) {
143fcf53d9bSJohn Marino 		if (__elf_phdr_match_addr(phdr_info, af->prepare) ||
144fcf53d9bSJohn Marino 		    __elf_phdr_match_addr(phdr_info, af->parent) ||
145fcf53d9bSJohn Marino 		    __elf_phdr_match_addr(phdr_info, af->child)) {
146fcf53d9bSJohn Marino 			TAILQ_REMOVE(&_thr_atfork_list, af, qe);
147e7bf3f77SMatthew Dillon 			__free(af);
148fcf53d9bSJohn Marino 		}
149fcf53d9bSJohn Marino 	}
150fcf53d9bSJohn Marino 	THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
151fcf53d9bSJohn Marino }
152fcf53d9bSJohn Marino 
15371b3fa15SDavid Xu /*
15471b3fa15SDavid Xu  * For a while, allow libpthread to work with a libc that doesn't
15571b3fa15SDavid Xu  * export the malloc lock.
15671b3fa15SDavid Xu  */
15771b3fa15SDavid Xu #pragma weak __malloc_lock
15871b3fa15SDavid Xu 
159fc71f871SDavid Xu pid_t _fork(void);
160fc71f871SDavid Xu 
16171b3fa15SDavid Xu pid_t
_fork(void)16271b3fa15SDavid Xu _fork(void)
16371b3fa15SDavid Xu {
16471b3fa15SDavid Xu 	static umtx_t inprogress;
16571b3fa15SDavid Xu 	static int waiters;
16671b3fa15SDavid Xu 	umtx_t tmp;
16771b3fa15SDavid Xu 
168*940be950Szrj 	pthread_t curthread;
16971b3fa15SDavid Xu 	struct pthread_atfork *af;
17071b3fa15SDavid Xu 	pid_t ret;
17171b3fa15SDavid Xu 	int errsave;
17271b3fa15SDavid Xu #ifndef __DragonFly__
17371b3fa15SDavid Xu 	int unlock_malloc;
17471b3fa15SDavid Xu #endif
17571b3fa15SDavid Xu 
17671b3fa15SDavid Xu 	if (!_thr_is_inited())
17782657471SMarkus Pfeiffer 		return (__syscall(SYS_fork));
17871b3fa15SDavid Xu 
17998247283SMatthew Dillon 	errsave = errno;
1809e2ee207SJoerg Sonnenberger 	curthread = tls_get_curthread();
18171b3fa15SDavid Xu 
18271b3fa15SDavid Xu 	THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
18371b3fa15SDavid Xu 	tmp = inprogress;
18471b3fa15SDavid Xu 	while (tmp) {
18571b3fa15SDavid Xu 		waiters++;
18671b3fa15SDavid Xu 		THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
1879219c44cSDavid Xu 		_thr_umtx_wait(&inprogress, tmp, NULL, 0);
18871b3fa15SDavid Xu 		THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
18971b3fa15SDavid Xu 		waiters--;
19071b3fa15SDavid Xu 		tmp = inprogress;
19171b3fa15SDavid Xu 	}
19271b3fa15SDavid Xu 	inprogress = 1;
19371b3fa15SDavid Xu 	THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
19471b3fa15SDavid Xu 
19571b3fa15SDavid Xu 	/* Run down atfork prepare handlers. */
19671b3fa15SDavid Xu 	TAILQ_FOREACH_REVERSE(af, &_thr_atfork_list, atfork_head, qe) {
19771b3fa15SDavid Xu 		if (af->prepare != NULL)
19871b3fa15SDavid Xu 			af->prepare();
19971b3fa15SDavid Xu 	}
20071b3fa15SDavid Xu 
20171b3fa15SDavid Xu #ifndef __DragonFly__
20271b3fa15SDavid Xu 	/*
20371b3fa15SDavid Xu 	 * Try our best to protect memory from being corrupted in
20471b3fa15SDavid Xu 	 * child process because another thread in malloc code will
20571b3fa15SDavid Xu 	 * simply be kill by fork().
20671b3fa15SDavid Xu 	 */
20771b3fa15SDavid Xu 	if ((_thr_isthreaded() != 0) && (__malloc_lock != NULL)) {
20871b3fa15SDavid Xu 		unlock_malloc = 1;
20971b3fa15SDavid Xu 		_spinlock(__malloc_lock);
21071b3fa15SDavid Xu 	} else {
21171b3fa15SDavid Xu 		unlock_malloc = 0;
21271b3fa15SDavid Xu 	}
21371b3fa15SDavid Xu #endif
21471b3fa15SDavid Xu 
215fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
216fcaa7a3aSMatthew Dillon 	_thr_log("fork-parent\n", 12);
217fcaa7a3aSMatthew Dillon #endif
21871b3fa15SDavid Xu 
21998247283SMatthew Dillon 	_thr_signal_block(curthread);
22098247283SMatthew Dillon 
22198247283SMatthew Dillon 	/*
22298247283SMatthew Dillon 	 * Must be executed Just before the fork.
22398247283SMatthew Dillon 	 */
22498247283SMatthew Dillon 	TAILQ_FOREACH_REVERSE(af, &_thr_atfork_kern_list, atfork_head, qe) {
22598247283SMatthew Dillon 		if (af->prepare != NULL)
22698247283SMatthew Dillon 			af->prepare();
22798247283SMatthew Dillon 	}
22898247283SMatthew Dillon 
22971b3fa15SDavid Xu 	/* Fork a new process: */
23082657471SMarkus Pfeiffer 	if ((ret = __syscall(SYS_fork)) == 0) {
23198247283SMatthew Dillon 		/*
23298247283SMatthew Dillon 		 * Child process.
23398247283SMatthew Dillon 		 *
23498247283SMatthew Dillon 		 * NOTE: We are using the saved errno from above.  Do not
23598247283SMatthew Dillon 		 *	 reload errno here.
23698247283SMatthew Dillon 		 */
23771b3fa15SDavid Xu 		inprogress = 0;
23898247283SMatthew Dillon 
23998247283SMatthew Dillon 		/*
24098247283SMatthew Dillon 		 * Internal child fork handlers must be run immediately.
24198247283SMatthew Dillon 		 */
24298247283SMatthew Dillon 		TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
24398247283SMatthew Dillon 			if (af->child != NULL)
24498247283SMatthew Dillon 				af->child();
24598247283SMatthew Dillon 		}
24698247283SMatthew Dillon 
24771b3fa15SDavid Xu 		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
24871b3fa15SDavid Xu 		/*
24971b3fa15SDavid Xu 		 * Thread list will be reinitialized, and later we call
25071b3fa15SDavid Xu 		 * _libpthread_init(), it will add us back to list.
25171b3fa15SDavid Xu 		 */
25271b3fa15SDavid Xu 		curthread->tlflags &= ~(TLFLAGS_IN_TDLIST | TLFLAGS_DETACHED);
25371b3fa15SDavid Xu 
25471b3fa15SDavid Xu 		/* child is a new kernel thread. */
25571b3fa15SDavid Xu 		curthread->tid = _thr_get_tid();
25671b3fa15SDavid Xu 
25771b3fa15SDavid Xu 		/* clear other threads locked us. */
25871b3fa15SDavid Xu 		_thr_umtx_init(&curthread->lock);
25971b3fa15SDavid Xu 		_thr_umtx_init(&_thr_atfork_lock);
26071b3fa15SDavid Xu 		_thr_setthreaded(0);
26171b3fa15SDavid Xu 
26271b3fa15SDavid Xu 		/* reinitialize libc spinlocks, this includes __malloc_lock. */
26371b3fa15SDavid Xu 		_thr_spinlock_init();
264fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
265fcaa7a3aSMatthew Dillon 		_thr_log("fork-child\n", 11);
266fcaa7a3aSMatthew Dillon #endif
2674cc8110fSMatthew Dillon 		_mutex_fork(curthread, curthread->tid);
26871b3fa15SDavid Xu 
26971b3fa15SDavid Xu 		/* reinitalize library. */
27071b3fa15SDavid Xu 		_libpthread_init(curthread);
27171b3fa15SDavid Xu 
27271b3fa15SDavid Xu 		/* Ready to continue, unblock signals. */
27371b3fa15SDavid Xu 		_thr_signal_unblock(curthread);
27471b3fa15SDavid Xu 
27571b3fa15SDavid Xu 		/* Run down atfork child handlers. */
27671b3fa15SDavid Xu 		TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
27771b3fa15SDavid Xu 			if (af->child != NULL)
27871b3fa15SDavid Xu 				af->child();
27971b3fa15SDavid Xu 		}
28071b3fa15SDavid Xu 	} else {
28171b3fa15SDavid Xu 		/* Parent process */
28271b3fa15SDavid Xu 		errsave = errno;
28371b3fa15SDavid Xu 
28471b3fa15SDavid Xu #ifndef __DragonFly__
28571b3fa15SDavid Xu 		if (unlock_malloc)
28671b3fa15SDavid Xu 			_spinunlock(__malloc_lock);
28771b3fa15SDavid Xu #endif
288fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
289fcaa7a3aSMatthew Dillon 		_thr_log("fork-done\n", 10);
290fcaa7a3aSMatthew Dillon #endif
29171b3fa15SDavid Xu 		/* Run down atfork parent handlers. */
292fcaa7a3aSMatthew Dillon 		TAILQ_FOREACH(af, &_thr_atfork_kern_list, qe) {
293fcaa7a3aSMatthew Dillon 			if (af->parent != NULL)
294fcaa7a3aSMatthew Dillon 				af->parent();
295fcaa7a3aSMatthew Dillon 		}
29698247283SMatthew Dillon 
29798247283SMatthew Dillon 		/* Ready to continue, unblock signals. */
29898247283SMatthew Dillon 		_thr_signal_unblock(curthread);
29998247283SMatthew Dillon 
30071b3fa15SDavid Xu 		TAILQ_FOREACH(af, &_thr_atfork_list, qe) {
30171b3fa15SDavid Xu 			if (af->parent != NULL)
30271b3fa15SDavid Xu 				af->parent();
30371b3fa15SDavid Xu 		}
30471b3fa15SDavid Xu 
30571b3fa15SDavid Xu 		THR_UMTX_LOCK(curthread, &_thr_atfork_lock);
30671b3fa15SDavid Xu 		inprogress = 0;
30771b3fa15SDavid Xu 		if (waiters)
30898247283SMatthew Dillon 			_thr_umtx_wake(&inprogress, 0);
30971b3fa15SDavid Xu 		THR_UMTX_UNLOCK(curthread, &_thr_atfork_lock);
31071b3fa15SDavid Xu 	}
31171b3fa15SDavid Xu 	errno = errsave;
31271b3fa15SDavid Xu 
31371b3fa15SDavid Xu 	/* Return the process ID: */
31471b3fa15SDavid Xu 	return (ret);
31571b3fa15SDavid Xu }
3165a1048c8SDavid Xu 
3175a1048c8SDavid Xu __strong_reference(_fork, fork);
3185a1048c8SDavid Xu __strong_reference(_pthread_atfork, pthread_atfork);
319