]> git.saurik.com Git - apple/libc.git/blob - threads/cthreads.c
3468d02c91da7ba9a74aed983a6252565a79d504
[apple/libc.git] / threads / cthreads.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 * cthreads.c - by Eric Cooper
25 *
26 * Implementation of cthread_fork, cthread_join, cthread_exit, etc.
27 * HISTORY
28 * 22-July-93 Blaine Garst
29 * fixed association of read_thread info
30 * fixed kernel cache set up of cproc info
31 *
32 */
33 #include "pthread_internals.h"
34 #include <stdlib.h>
35 #include <sys/queue.h>
36 #include "cthreads.h"
37 #include "cthread_internals.h"
38 /*
39 * C Threads imports:
40 */
41 extern void cproc_init();
42 extern thread_port_t cproc_create();
43 extern void mig_init();
44 extern void _pthread_set_self(pthread_t);
45
46 /*
47 * Mach imports:
48 */
49 extern void mig_fork_child();
50
51 /*
52 * C library imports:
53 */
54 extern int _setjmp(jmp_buf env);
55 extern void _longjmp(jmp_buf env, int val);
56
57 /*
58 * Thread status bits.
59 */
60 #define T_MAIN 0x1
61 #define T_RETURNED 0x2
62 #define T_DETACHED 0x4
63
64 #ifdef CTHREADS_DEBUG
65 int cthread_debug = FALSE;
66 #endif /* CTHREADS_DEBUG */
67
68 /*
69 * Routines for supporting fork() of multi-threaded programs.
70 */
71
72
73 extern void _malloc_fork_prepare(), _malloc_fork_parent();
74 extern void _malloc_fork_child();
75 extern void fork_mach_init();
76 extern void _cproc_fork_child(), _stack_fork_child();
77 extern void _lu_fork_child(void);
78 extern void _pthread_fork_child(pthread_t);
79 extern void _notify_fork_child(void);
80
81 static pthread_t psaved_self = 0;
82 static pthread_lock_t psaved_self_global_lock = LOCK_INITIALIZER;
83 static pthread_lock_t pthread_atfork_lock = LOCK_INITIALIZER;
84 struct pthread_atfork_entry {
85 TAILQ_ENTRY(pthread_atfork_entry) qentry;
86 void (*prepare)(void);
87 void (*parent)(void);
88 void (*child)(void);
89 };
90 static TAILQ_HEAD(pthread_atfork_queue_head, pthread_atfork_entry) pthread_atfork_queue = TAILQ_HEAD_INITIALIZER(pthread_atfork_queue);
91
92 int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
93 {
94 struct pthread_atfork_entry *e;
95
96 e = malloc(sizeof(struct pthread_atfork_entry));
97 if (e == NULL)
98 return (ENOMEM);
99
100 e->prepare = prepare;
101 e->parent = parent;
102 e->child = child;
103
104 _spin_lock(&pthread_atfork_lock);
105 TAILQ_INSERT_TAIL(&pthread_atfork_queue, e, qentry);
106 _spin_unlock(&pthread_atfork_lock);
107
108 return 0;
109 }
110
111 void _cthread_fork_prepare()
112 /*
113 * Prepare cthreads library to fork() a multi-threaded program. All cthread
114 * library critical section locks are acquired before a fork() and released
115 * afterwards to insure no cthread data structure is left in an inconsistent
116 * state in the child, which comes up with only the forking thread running.
117 */
118 {
119 struct pthread_atfork_entry *e;
120
121 _spin_lock(&pthread_atfork_lock);
122 #ifdef TAILQ_FOREACH_REVERSE_LEGACY_ORDER
123 TAILQ_FOREACH_REVERSE(e, &pthread_atfork_queue, qentry, pthread_atfork_queue_head)
124 #else /* !TAILQ_FOREACH_REVERSE_LEGACY_ORDER */
125 TAILQ_FOREACH_REVERSE(e, &pthread_atfork_queue, pthread_atfork_queue_head, qentry)
126 #endif /* TAILQ_FOREACH_REVERSE_LEGACY_ORDER */
127 {
128 if (e->prepare != NULL)
129 e->prepare();
130 }
131
132 _spin_lock(&psaved_self_global_lock);
133 psaved_self = pthread_self();
134 _spin_lock(&psaved_self->lock);
135 _malloc_fork_prepare();
136 }
137
138 void _cthread_fork_parent()
139 /*
140 * Called in the parent process after a fork syscall.
141 * Releases locks acquired by cthread_fork_prepare().
142 */
143 {
144 struct pthread_atfork_entry *e;
145
146 _malloc_fork_parent();
147 _spin_unlock(&psaved_self->lock);
148 _spin_unlock(&psaved_self_global_lock);
149
150 TAILQ_FOREACH(e, &pthread_atfork_queue, qentry) {
151 if (e->parent != NULL)
152 e->parent();
153 }
154 _spin_unlock(&pthread_atfork_lock);
155
156 }
157
158 void _cthread_fork_child()
159 /*
160 * Called in the child process after a fork syscall. Releases locks acquired
161 * by cthread_fork_prepare(). Deallocates cthread data structures which
162 * described other threads in our parent. Makes this thread the main thread.
163 */
164 {
165 pthread_t p = psaved_self;
166 struct pthread_atfork_entry *e;
167
168 _pthread_set_self(p);
169 _spin_unlock(&psaved_self_global_lock);
170 mig_fork_child();
171 _malloc_fork_child();
172 p->kernel_thread = mach_thread_self();
173 p->reply_port = mach_reply_port();
174 p->mutexes = NULL;
175 p->__cleanup_stack = NULL;
176 p->death = MACH_PORT_NULL;
177 p->joiner = NULL;
178 p->detached |= _PTHREAD_CREATE_PARENT;
179 _spin_unlock(&p->lock);
180
181 fork_mach_init();
182 _pthread_fork_child(p);
183
184 _cproc_fork_child();
185
186 _lu_fork_child();
187
188 _notify_fork_child();
189
190 __is_threaded = 0;
191
192 mig_init(1); /* enable multi-threaded mig interfaces */
193
194 TAILQ_FOREACH(e, &pthread_atfork_queue, qentry) {
195 if (e->child != NULL)
196 e->child();
197 }
198 LOCK_INIT(pthread_atfork_lock);
199
200 }
201