]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2000-2013 Apple 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 | * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991 | |
25 | * All Rights Reserved | |
26 | * | |
27 | * Permission to use, copy, modify, and distribute this software and | |
28 | * its documentation for any purpose and without fee is hereby granted, | |
29 | * provided that the above copyright notice appears in all copies and | |
30 | * that both the copyright notice and this permission notice appear in | |
31 | * supporting documentation. | |
32 | * | |
33 | * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE | |
34 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
35 | * FOR A PARTICULAR PURPOSE. | |
36 | * | |
37 | * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR | |
38 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | |
39 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, | |
40 | * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION | |
41 | * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
42 | * | |
43 | */ | |
44 | /* | |
45 | * MkLinux | |
46 | */ | |
47 | ||
48 | /* | |
49 | * POSIX Pthread Library | |
50 | */ | |
51 | ||
52 | #include "internal.h" | |
53 | ||
54 | #include <stdio.h> /* For printf(). */ | |
55 | #include <stdlib.h> | |
56 | #include <errno.h> /* For __mach_errno_addr() prototype. */ | |
57 | #include <signal.h> | |
58 | #include <sys/time.h> | |
59 | #include <sys/resource.h> | |
60 | #include <sys/sysctl.h> | |
61 | #include <sys/queue.h> | |
62 | #include <machine/vmparam.h> | |
63 | #include <mach/vm_statistics.h> | |
64 | ||
65 | extern int __unix_conforming; | |
66 | extern int _pthread_setcancelstate_internal(int state, int *oldstate, int conforming); | |
67 | extern void _pthread_testcancel(pthread_t thread, int isconforming); | |
68 | extern int _pthread_cond_wait(pthread_cond_t *cond, | |
69 | pthread_mutex_t *mutex, | |
70 | const struct timespec *abstime, | |
71 | int isRelative, | |
72 | int isconforming); | |
73 | extern int __sigwait(const sigset_t *set, int *sig); | |
74 | extern int __pthread_sigmask(int, const sigset_t *, sigset_t *); | |
75 | extern int __pthread_markcancel(int); | |
76 | ||
77 | #ifdef VARIANT_CANCELABLE | |
78 | extern int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, __int64_t tv_sec, __int32_t tv_nsec); | |
79 | #else | |
80 | extern int __semwait_signal(int cond_sem, int mutex_sem, int timeout, int relative, __int64_t tv_sec, __int32_t tv_nsec) __asm__("___semwait_signal_nocancel"); | |
81 | #endif | |
82 | ||
83 | #ifndef VARIANT_CANCELABLE | |
84 | ||
85 | /* | |
86 | * Cancel a thread | |
87 | */ | |
88 | int | |
89 | pthread_cancel(pthread_t thread) | |
90 | { | |
91 | #if __DARWIN_UNIX03 | |
92 | if (__unix_conforming == 0) | |
93 | __unix_conforming = 1; | |
94 | #endif /* __DARWIN_UNIX03 */ | |
95 | ||
96 | if (_pthread_lookup_thread(thread, NULL, 0) != 0) | |
97 | return(ESRCH); | |
98 | ||
99 | /* if the thread is a workqueue thread, then return error */ | |
100 | if (thread->wqthread != 0) { | |
101 | return(ENOTSUP); | |
102 | } | |
103 | #if __DARWIN_UNIX03 | |
104 | int state; | |
105 | ||
106 | _PTHREAD_LOCK(thread->lock); | |
107 | state = thread->cancel_state |= _PTHREAD_CANCEL_PENDING; | |
108 | _PTHREAD_UNLOCK(thread->lock); | |
109 | if (state & PTHREAD_CANCEL_ENABLE) | |
110 | __pthread_markcancel(_pthread_kernel_thread(thread)); | |
111 | #else /* __DARWIN_UNIX03 */ | |
112 | thread->cancel_state |= _PTHREAD_CANCEL_PENDING; | |
113 | #endif /* __DARWIN_UNIX03 */ | |
114 | return (0); | |
115 | } | |
116 | ||
117 | void | |
118 | pthread_testcancel(void) | |
119 | { | |
120 | pthread_t self = pthread_self(); | |
121 | ||
122 | #if __DARWIN_UNIX03 | |
123 | if (__unix_conforming == 0) | |
124 | __unix_conforming = 1; | |
125 | _pthread_testcancel(self, 1); | |
126 | #else /* __DARWIN_UNIX03 */ | |
127 | _pthread_testcancel(self, 0); | |
128 | #endif /* __DARWIN_UNIX03 */ | |
129 | } | |
130 | ||
131 | /* | |
132 | * Query/update the cancelability 'state' of a thread | |
133 | */ | |
134 | int | |
135 | pthread_setcancelstate(int state, int *oldstate) | |
136 | { | |
137 | #if __DARWIN_UNIX03 | |
138 | if (__unix_conforming == 0) { | |
139 | __unix_conforming = 1; | |
140 | } | |
141 | return (_pthread_setcancelstate_internal(state, oldstate, 1)); | |
142 | #else /* __DARWIN_UNIX03 */ | |
143 | return (_pthread_setcancelstate_internal(state, oldstate, 0)); | |
144 | #endif /* __DARWIN_UNIX03 */ | |
145 | } | |
146 | ||
147 | /* | |
148 | * Query/update the cancelability 'type' of a thread | |
149 | */ | |
150 | int | |
151 | pthread_setcanceltype(int type, int *oldtype) | |
152 | { | |
153 | pthread_t self; | |
154 | ||
155 | #if __DARWIN_UNIX03 | |
156 | if (__unix_conforming == 0) | |
157 | __unix_conforming = 1; | |
158 | #endif /* __DARWIN_UNIX03 */ | |
159 | ||
160 | if ((type != PTHREAD_CANCEL_DEFERRED) && | |
161 | (type != PTHREAD_CANCEL_ASYNCHRONOUS)) | |
162 | return EINVAL; | |
163 | self = pthread_self(); | |
164 | _PTHREAD_LOCK(self->lock); | |
165 | if (oldtype) | |
166 | *oldtype = self->cancel_state & _PTHREAD_CANCEL_TYPE_MASK; | |
167 | self->cancel_state &= ~_PTHREAD_CANCEL_TYPE_MASK; | |
168 | self->cancel_state |= type; | |
169 | _PTHREAD_UNLOCK(self->lock); | |
170 | #if !__DARWIN_UNIX03 | |
171 | _pthread_testcancel(self, 0); /* See if we need to 'die' now... */ | |
172 | #endif /* __DARWIN_UNIX03 */ | |
173 | return (0); | |
174 | } | |
175 | ||
176 | int | |
177 | pthread_sigmask(int how, const sigset_t * set, sigset_t * oset) | |
178 | { | |
179 | #if __DARWIN_UNIX03 | |
180 | int err = 0; | |
181 | ||
182 | if (__pthread_sigmask(how, set, oset) == -1) { | |
183 | err = errno; | |
184 | } | |
185 | return(err); | |
186 | #else /* __DARWIN_UNIX03 */ | |
187 | return(__pthread_sigmask(how, set, oset)); | |
188 | #endif /* __DARWIN_UNIX03 */ | |
189 | } | |
190 | ||
191 | #endif /* VARIANT_CANCELABLE */ | |
192 | ||
193 | #if __DARWIN_UNIX03 | |
194 | ||
195 | static void | |
196 | __posix_join_cleanup(void *arg) | |
197 | { | |
198 | pthread_t thread = (pthread_t)arg; | |
199 | ||
200 | _PTHREAD_LOCK(thread->lock); | |
201 | /* leave another thread to join */ | |
202 | thread->joiner = (struct _pthread *)NULL; | |
203 | _PTHREAD_UNLOCK(thread->lock); | |
204 | } | |
205 | ||
206 | #endif /* __DARWIN_UNIX03 */ | |
207 | ||
208 | /* | |
209 | * Wait for a thread to terminate and obtain its exit value. | |
210 | */ | |
211 | int | |
212 | pthread_join(pthread_t thread, | |
213 | void **value_ptr) | |
214 | { | |
215 | int res = 0; | |
216 | pthread_t self = pthread_self(); | |
217 | mach_port_t kthport; | |
218 | int conforming = 0; | |
219 | #if !__DARWIN_UNIX03 | |
220 | kern_return_t kern_res; | |
221 | #endif | |
222 | ||
223 | #if __DARWIN_UNIX03 | |
224 | if (__unix_conforming == 0) | |
225 | __unix_conforming = 1; | |
226 | ||
227 | #ifdef VARIANT_CANCELABLE | |
228 | _pthread_testcancel(self, 1); | |
229 | #endif /* VARIANT_CANCELABLE */ | |
230 | #endif /* __DARWIN_UNIX03 */ | |
231 | ||
232 | if ((res = _pthread_lookup_thread(thread, &kthport, 1)) != 0) | |
233 | return(res); | |
234 | ||
235 | if (thread->sig == _PTHREAD_SIG) { | |
236 | semaphore_t death = SEMAPHORE_NULL; /* in case we need it */ | |
237 | semaphore_t joinsem = SEMAPHORE_NULL; | |
238 | ||
239 | if (thread->joiner_notify == SEMAPHORE_NULL) { | |
240 | death = (semaphore_t)os_get_cached_semaphore(); | |
241 | } | |
242 | ||
243 | _PTHREAD_LOCK(thread->lock); | |
244 | if ((thread->detached & PTHREAD_CREATE_JOINABLE) && | |
245 | (thread->joiner == NULL)) { | |
246 | PTHREAD_ASSERT(_pthread_kernel_thread(thread) == kthport); | |
247 | if (thread != self && (self == NULL || self->joiner != thread)) { | |
248 | if (thread->joiner_notify == SEMAPHORE_NULL) { | |
249 | thread->joiner_notify = death; | |
250 | death = SEMAPHORE_NULL; | |
251 | } | |
252 | joinsem = thread->joiner_notify; | |
253 | thread->joiner = self; | |
254 | _PTHREAD_UNLOCK(thread->lock); | |
255 | ||
256 | if (death != SEMAPHORE_NULL) { | |
257 | os_put_cached_semaphore((os_semaphore_t)death); | |
258 | death = SEMAPHORE_NULL; | |
259 | } | |
260 | #if __DARWIN_UNIX03 | |
261 | /* Wait for it to signal... */ | |
262 | pthread_cleanup_push(__posix_join_cleanup, (void *)thread); | |
263 | do { | |
264 | res = __semwait_signal(joinsem, 0, 0, 0, (int64_t)0, (int32_t)0); | |
265 | } while ((res < 0) && (errno == EINTR)); | |
266 | pthread_cleanup_pop(0); | |
267 | #else /* __DARWIN_UNIX03 */ | |
268 | /* Wait for it to signal... */ | |
269 | do { | |
270 | kern_res = semaphore_wait(joinsem); | |
271 | } while (kern_res != KERN_SUCCESS); | |
272 | #endif /* __DARWIN_UNIX03 */ | |
273 | ||
274 | os_put_cached_semaphore((os_semaphore_t)joinsem); | |
275 | res = _pthread_join_cleanup(thread, value_ptr, conforming); | |
276 | } else { | |
277 | _PTHREAD_UNLOCK(thread->lock); | |
278 | res = EDEADLK; | |
279 | } | |
280 | } else { | |
281 | _PTHREAD_UNLOCK(thread->lock); | |
282 | res = EINVAL; | |
283 | } | |
284 | if (death != SEMAPHORE_NULL) { | |
285 | os_put_cached_semaphore((os_semaphore_t)death); | |
286 | } | |
287 | return res; | |
288 | } | |
289 | return ESRCH; | |
290 | } | |
291 | ||
292 | int | |
293 | pthread_cond_wait(pthread_cond_t *cond, | |
294 | pthread_mutex_t *mutex) | |
295 | { | |
296 | int conforming; | |
297 | #if __DARWIN_UNIX03 | |
298 | ||
299 | if (__unix_conforming == 0) | |
300 | __unix_conforming = 1; | |
301 | ||
302 | #ifdef VARIANT_CANCELABLE | |
303 | conforming = 1; | |
304 | #else /* !VARIANT_CANCELABLE */ | |
305 | conforming = -1; | |
306 | #endif /* VARIANT_CANCELABLE */ | |
307 | #else /* __DARWIN_UNIX03 */ | |
308 | conforming = 0; | |
309 | #endif /* __DARWIN_UNIX03 */ | |
310 | return (_pthread_cond_wait(cond, mutex, (struct timespec *)NULL, 0, conforming)); | |
311 | } | |
312 | ||
313 | int | |
314 | pthread_cond_timedwait(pthread_cond_t *cond, | |
315 | pthread_mutex_t *mutex, | |
316 | const struct timespec *abstime) | |
317 | { | |
318 | int conforming; | |
319 | #if __DARWIN_UNIX03 | |
320 | if (__unix_conforming == 0) | |
321 | __unix_conforming = 1; | |
322 | ||
323 | #ifdef VARIANT_CANCELABLE | |
324 | conforming = 1; | |
325 | #else /* !VARIANT_CANCELABLE */ | |
326 | conforming = -1; | |
327 | #endif /* VARIANT_CANCELABLE */ | |
328 | #else /* __DARWIN_UNIX03 */ | |
329 | conforming = 0; | |
330 | #endif /* __DARWIN_UNIX03 */ | |
331 | ||
332 | return (_pthread_cond_wait(cond, mutex, abstime, 0, conforming)); | |
333 | } | |
334 | ||
335 | int | |
336 | sigwait(const sigset_t * set, int * sig) | |
337 | { | |
338 | #if __DARWIN_UNIX03 | |
339 | int err = 0; | |
340 | ||
341 | if (__unix_conforming == 0) | |
342 | __unix_conforming = 1; | |
343 | ||
344 | #ifdef VARIANT_CANCELABLE | |
345 | _pthread_testcancel(pthread_self(), 1); | |
346 | #endif /* VARIANT_CANCELABLE */ | |
347 | ||
348 | if (__sigwait(set, sig) == -1) { | |
349 | err = errno; | |
350 | ||
351 | #ifdef VARIANT_CANCELABLE | |
352 | _pthread_testcancel(pthread_self(), 1); | |
353 | #endif /* VARIANT_CANCELABLE */ | |
354 | ||
355 | /* | |
356 | * EINTR that isn't a result of pthread_cancel() | |
357 | * is translated to 0. | |
358 | */ | |
359 | if (err == EINTR) { | |
360 | err = 0; | |
361 | } | |
362 | } | |
363 | return(err); | |
364 | #else /* __DARWIN_UNIX03 */ | |
365 | if (__sigwait(set, sig) == -1) { | |
366 | /* | |
367 | * EINTR that isn't a result of pthread_cancel() | |
368 | * is translated to 0. | |
369 | */ | |
370 | if (errno != EINTR) { | |
371 | return -1; | |
372 | } | |
373 | } | |
374 | ||
375 | return 0; | |
376 | #endif /* __DARWIN_UNIX03 */ | |
377 | } |