/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * cthreads.c - by Eric Cooper * * Implementation of cthread_fork, cthread_join, cthread_exit, etc. * HISTORY * 22-July-93 Blaine Garst * fixed association of read_thread info * fixed kernel cache set up of cproc info * */ #include "pthread_internals.h" #include #include #include "cthreads.h" #include "cthread_internals.h" /* * C Threads imports: */ extern void cproc_init(); extern thread_port_t cproc_create(); extern void mig_init(); extern void _pthread_set_self(pthread_t); /* * Mach imports: */ extern void mig_fork_child(); /* * C library imports: */ extern int _setjmp(jmp_buf env); extern void _longjmp(jmp_buf env, int val); /* * Thread status bits. */ #define T_MAIN 0x1 #define T_RETURNED 0x2 #define T_DETACHED 0x4 #ifdef CTHREADS_DEBUG int cthread_debug = FALSE; #endif /* CTHREADS_DEBUG */ /* * Routines for supporting fork() of multi-threaded programs. */ extern void _malloc_fork_prepare(), _malloc_fork_parent(); extern void _malloc_fork_child(); extern void fork_mach_init(); extern void _cproc_fork_child(), _stack_fork_child(); extern void _lu_fork_child(void); extern void _pthread_fork_child(pthread_t); extern void _notify_fork_child(void); static pthread_t psaved_self = 0; static pthread_lock_t psaved_self_global_lock = LOCK_INITIALIZER; static pthread_lock_t pthread_atfork_lock = LOCK_INITIALIZER; struct pthread_atfork_entry { TAILQ_ENTRY(pthread_atfork_entry) qentry; void (*prepare)(void); void (*parent)(void); void (*child)(void); }; static TAILQ_HEAD(pthread_atfork_queue_head, pthread_atfork_entry) pthread_atfork_queue = TAILQ_HEAD_INITIALIZER(pthread_atfork_queue); int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { struct pthread_atfork_entry *e; e = malloc(sizeof(struct pthread_atfork_entry)); if (e == NULL) return (ENOMEM); e->prepare = prepare; e->parent = parent; e->child = child; _spin_lock(&pthread_atfork_lock); TAILQ_INSERT_TAIL(&pthread_atfork_queue, e, qentry); _spin_unlock(&pthread_atfork_lock); return 0; } void _cthread_fork_prepare() /* * Prepare cthreads library to fork() a multi-threaded program. All cthread * library critical section locks are acquired before a fork() and released * afterwards to insure no cthread data structure is left in an inconsistent * state in the child, which comes up with only the forking thread running. */ { struct pthread_atfork_entry *e; _spin_lock(&pthread_atfork_lock); TAILQ_FOREACH_REVERSE(e, &pthread_atfork_queue, qentry, pthread_atfork_queue_head) { if (e->prepare != NULL) e->prepare(); } _spin_lock(&psaved_self_global_lock); psaved_self = pthread_self(); _spin_lock(&psaved_self->lock); _malloc_fork_prepare(); } void _cthread_fork_parent() /* * Called in the parent process after a fork syscall. * Releases locks acquired by cthread_fork_prepare(). */ { struct pthread_atfork_entry *e; _malloc_fork_parent(); _spin_unlock(&psaved_self->lock); _spin_unlock(&psaved_self_global_lock); TAILQ_FOREACH(e, &pthread_atfork_queue, qentry) { if (e->parent != NULL) e->parent(); } _spin_unlock(&pthread_atfork_lock); } void _cthread_fork_child() /* * Called in the child process after a fork syscall. Releases locks acquired * by cthread_fork_prepare(). Deallocates cthread data structures which * described other threads in our parent. Makes this thread the main thread. */ { pthread_t p = psaved_self; struct pthread_atfork_entry *e; _pthread_set_self(p); _spin_unlock(&psaved_self_global_lock); mig_fork_child(); _malloc_fork_child(); p->kernel_thread = mach_thread_self(); p->reply_port = mach_reply_port(); p->mutexes = NULL; p->__cleanup_stack = NULL; p->death = MACH_PORT_NULL; p->joiner = NULL; p->detached |= _PTHREAD_CREATE_PARENT; _spin_unlock(&p->lock); fork_mach_init(); _pthread_fork_child(p); _cproc_fork_child(); _lu_fork_child(); _notify_fork_child(); __is_threaded = 0; mig_init(1); /* enable multi-threaded mig interfaces */ TAILQ_FOREACH(e, &pthread_atfork_queue, qentry) { if (e->child != NULL) e->child(); } LOCK_INIT(pthread_atfork_lock); }