]> git.saurik.com Git - apple/libdispatch.git/blob - src/semaphore.c
libdispatch-500.10.1.tar.gz
[apple/libdispatch.git] / src / semaphore.c
1 /*
2 * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 #include "internal.h"
22
23 // semaphores are too fundamental to use the dispatch_assume*() macros
24 #if USE_MACH_SEM
25 #define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \
26 if (slowpath((x) == KERN_INVALID_NAME)) { \
27 DISPATCH_CLIENT_CRASH("Use-after-free of dispatch_semaphore_t"); \
28 } else if (slowpath(x)) { \
29 DISPATCH_CRASH("mach semaphore API failure"); \
30 } \
31 } while (0)
32 #define DISPATCH_GROUP_VERIFY_KR(x) do { \
33 if (slowpath((x) == KERN_INVALID_NAME)) { \
34 DISPATCH_CLIENT_CRASH("Use-after-free of dispatch_group_t"); \
35 } else if (slowpath(x)) { \
36 DISPATCH_CRASH("mach semaphore API failure"); \
37 } \
38 } while (0)
39 #elif USE_POSIX_SEM
40 #define DISPATCH_SEMAPHORE_VERIFY_RET(x) do { \
41 if (slowpath((x) == -1)) { \
42 DISPATCH_CRASH("POSIX semaphore API failure"); \
43 } \
44 } while (0)
45 #endif
46
47 #if USE_WIN32_SEM
48 // rdar://problem/8428132
49 static DWORD best_resolution = 1; // 1ms
50
51 DWORD
52 _push_timer_resolution(DWORD ms)
53 {
54 MMRESULT res;
55 static dispatch_once_t once;
56
57 if (ms > 16) {
58 // only update timer resolution if smaller than default 15.6ms
59 // zero means not updated
60 return 0;
61 }
62
63 // aim for the best resolution we can accomplish
64 dispatch_once(&once, ^{
65 TIMECAPS tc;
66 MMRESULT res;
67 res = timeGetDevCaps(&tc, sizeof(tc));
68 if (res == MMSYSERR_NOERROR) {
69 best_resolution = min(max(tc.wPeriodMin, best_resolution),
70 tc.wPeriodMax);
71 }
72 });
73
74 res = timeBeginPeriod(best_resolution);
75 if (res == TIMERR_NOERROR) {
76 return best_resolution;
77 }
78 // zero means not updated
79 return 0;
80 }
81
82 // match ms parameter to result from _push_timer_resolution
83 void
84 _pop_timer_resolution(DWORD ms)
85 {
86 if (ms) {
87 timeEndPeriod(ms);
88 }
89 }
90 #endif /* USE_WIN32_SEM */
91
92
93 DISPATCH_WEAK // rdar://problem/8503746
94 long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema);
95
96 #pragma mark -
97 #pragma mark dispatch_semaphore_t
98
99 static void
100 _dispatch_semaphore_init(long value, dispatch_object_t dou)
101 {
102 dispatch_semaphore_t dsema = dou._dsema;
103
104 dsema->do_next = (dispatch_semaphore_t)DISPATCH_OBJECT_LISTLESS;
105 dsema->do_targetq = _dispatch_get_root_queue(_DISPATCH_QOS_CLASS_DEFAULT,
106 false);
107 dsema->dsema_value = value;
108 dsema->dsema_orig = value;
109 #if USE_POSIX_SEM
110 int ret = sem_init(&dsema->dsema_sem, 0, 0);
111 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
112 #endif
113 }
114
115 dispatch_semaphore_t
116 dispatch_semaphore_create(long value)
117 {
118 dispatch_semaphore_t dsema;
119
120 // If the internal value is negative, then the absolute of the value is
121 // equal to the number of waiting threads. Therefore it is bogus to
122 // initialize the semaphore with a negative value.
123 if (value < 0) {
124 return NULL;
125 }
126
127 dsema = (dispatch_semaphore_t)_dispatch_alloc(DISPATCH_VTABLE(semaphore),
128 sizeof(struct dispatch_semaphore_s) -
129 sizeof(dsema->dsema_notify_head) -
130 sizeof(dsema->dsema_notify_tail));
131 _dispatch_semaphore_init(value, dsema);
132 return dsema;
133 }
134
135 #if USE_MACH_SEM
136 static void
137 _dispatch_semaphore_create_port(semaphore_t *s4)
138 {
139 kern_return_t kr;
140 semaphore_t tmp;
141
142 if (*s4) {
143 return;
144 }
145 _dispatch_safe_fork = false;
146
147 // lazily allocate the semaphore port
148
149 // Someday:
150 // 1) Switch to a doubly-linked FIFO in user-space.
151 // 2) User-space timers for the timeout.
152 // 3) Use the per-thread semaphore port.
153
154 while ((kr = semaphore_create(mach_task_self(), &tmp,
155 SYNC_POLICY_FIFO, 0))) {
156 DISPATCH_VERIFY_MIG(kr);
157 _dispatch_temporary_resource_shortage();
158 }
159
160 if (!dispatch_atomic_cmpxchg(s4, 0, tmp, relaxed)) {
161 kr = semaphore_destroy(mach_task_self(), tmp);
162 DISPATCH_VERIFY_MIG(kr);
163 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
164 }
165 }
166 #elif USE_WIN32_SEM
167 static void
168 _dispatch_semaphore_create_handle(HANDLE *s4)
169 {
170 HANDLE tmp;
171
172 if (*s4) {
173 return;
174 }
175
176 // lazily allocate the semaphore port
177
178 while (!dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL))) {
179 _dispatch_temporary_resource_shortage();
180 }
181
182 if (!dispatch_atomic_cmpxchg(s4, 0, tmp)) {
183 CloseHandle(tmp);
184 }
185 }
186 #endif
187
188 void
189 _dispatch_semaphore_dispose(dispatch_object_t dou)
190 {
191 dispatch_semaphore_t dsema = dou._dsema;
192
193 if (dsema->dsema_value < dsema->dsema_orig) {
194 DISPATCH_CLIENT_CRASH(
195 "Semaphore/group object deallocated while in use");
196 }
197
198 #if USE_MACH_SEM
199 kern_return_t kr;
200 if (dsema->dsema_port) {
201 kr = semaphore_destroy(mach_task_self(), dsema->dsema_port);
202 DISPATCH_VERIFY_MIG(kr);
203 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
204 }
205 dsema->dsema_port = MACH_PORT_DEAD;
206 #elif USE_POSIX_SEM
207 int ret = sem_destroy(&dsema->dsema_sem);
208 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
209 #elif USE_WIN32_SEM
210 if (dsema->dsema_handle) {
211 CloseHandle(dsema->dsema_handle);
212 }
213 #endif
214 }
215
216 size_t
217 _dispatch_semaphore_debug(dispatch_object_t dou, char *buf, size_t bufsiz)
218 {
219 dispatch_semaphore_t dsema = dou._dsema;
220
221 size_t offset = 0;
222 offset += dsnprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ",
223 dx_kind(dsema), dsema);
224 offset += _dispatch_object_debug_attr(dsema, &buf[offset], bufsiz - offset);
225 #if USE_MACH_SEM
226 offset += dsnprintf(&buf[offset], bufsiz - offset, "port = 0x%u, ",
227 dsema->dsema_port);
228 #endif
229 offset += dsnprintf(&buf[offset], bufsiz - offset,
230 "value = %ld, orig = %ld }", dsema->dsema_value, dsema->dsema_orig);
231 return offset;
232 }
233
234 DISPATCH_NOINLINE
235 long
236 _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
237 {
238 // Before dsema_sent_ksignals is incremented we can rely on the reference
239 // held by the waiter. However, once this value is incremented the waiter
240 // may return between the atomic increment and the semaphore_signal(),
241 // therefore an explicit reference must be held in order to safely access
242 // dsema after the atomic increment.
243 _dispatch_retain(dsema);
244
245 #if USE_MACH_SEM || USE_POSIX_SEM
246 (void)dispatch_atomic_inc2o(dsema, dsema_sent_ksignals, relaxed);
247 #endif
248
249 #if USE_MACH_SEM
250 _dispatch_semaphore_create_port(&dsema->dsema_port);
251 kern_return_t kr = semaphore_signal(dsema->dsema_port);
252 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
253 #elif USE_POSIX_SEM
254 int ret = sem_post(&dsema->dsema_sem);
255 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
256 #elif USE_WIN32_SEM
257 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
258 int ret = ReleaseSemaphore(dsema->dsema_handle, 1, NULL);
259 dispatch_assume(ret);
260 #endif
261
262 _dispatch_release(dsema);
263 return 1;
264 }
265
266 long
267 dispatch_semaphore_signal(dispatch_semaphore_t dsema)
268 {
269 long value = dispatch_atomic_inc2o(dsema, dsema_value, release);
270 if (fastpath(value > 0)) {
271 return 0;
272 }
273 if (slowpath(value == LONG_MIN)) {
274 DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_semaphore_signal()");
275 }
276 return _dispatch_semaphore_signal_slow(dsema);
277 }
278
279 DISPATCH_NOINLINE
280 static long
281 _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,
282 dispatch_time_t timeout)
283 {
284 long orig;
285
286 #if USE_MACH_SEM
287 mach_timespec_t _timeout;
288 kern_return_t kr;
289 #elif USE_POSIX_SEM
290 struct timespec _timeout;
291 int ret;
292 #elif USE_WIN32_SEM
293 uint64_t nsec;
294 DWORD msec;
295 DWORD resolution;
296 DWORD wait_result;
297 #endif
298
299 #if USE_MACH_SEM || USE_POSIX_SEM
300 again:
301 // Mach semaphores appear to sometimes spuriously wake up. Therefore,
302 // we keep a parallel count of the number of times a Mach semaphore is
303 // signaled (6880961).
304 orig = dsema->dsema_sent_ksignals;
305 while (orig) {
306 if (dispatch_atomic_cmpxchgvw2o(dsema, dsema_sent_ksignals, orig,
307 orig - 1, &orig, relaxed)) {
308 return 0;
309 }
310 }
311 #endif
312
313 #if USE_MACH_SEM
314 _dispatch_semaphore_create_port(&dsema->dsema_port);
315 #elif USE_WIN32_SEM
316 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
317 #endif
318
319 // From xnu/osfmk/kern/sync_sema.c:
320 // wait_semaphore->count = -1; /* we don't keep an actual count */
321 //
322 // The code above does not match the documentation, and that fact is
323 // not surprising. The documented semantics are clumsy to use in any
324 // practical way. The above hack effectively tricks the rest of the
325 // Mach semaphore logic to behave like the libdispatch algorithm.
326
327 switch (timeout) {
328 default:
329 #if USE_MACH_SEM
330 do {
331 uint64_t nsec = _dispatch_timeout(timeout);
332 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
333 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
334 kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout));
335 } while (kr == KERN_ABORTED);
336
337 if (kr != KERN_OPERATION_TIMED_OUT) {
338 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
339 break;
340 }
341 #elif USE_POSIX_SEM
342 do {
343 uint64_t nsec = _dispatch_timeout(timeout);
344 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
345 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
346 ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout));
347 } while (ret == -1 && errno == EINTR);
348
349 if (ret == -1 && errno != ETIMEDOUT) {
350 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
351 break;
352 }
353 #elif USE_WIN32_SEM
354 nsec = _dispatch_timeout(timeout);
355 msec = (DWORD)(nsec / (uint64_t)1000000);
356 resolution = _push_timer_resolution(msec);
357 wait_result = WaitForSingleObject(dsema->dsema_handle, msec);
358 _pop_timer_resolution(resolution);
359 if (wait_result != WAIT_TIMEOUT) {
360 break;
361 }
362 #endif
363 // Fall through and try to undo what the fast path did to
364 // dsema->dsema_value
365 case DISPATCH_TIME_NOW:
366 orig = dsema->dsema_value;
367 while (orig < 0) {
368 if (dispatch_atomic_cmpxchgvw2o(dsema, dsema_value, orig, orig + 1,
369 &orig, relaxed)) {
370 #if USE_MACH_SEM
371 return KERN_OPERATION_TIMED_OUT;
372 #elif USE_POSIX_SEM || USE_WIN32_SEM
373 errno = ETIMEDOUT;
374 return -1;
375 #endif
376 }
377 }
378 // Another thread called semaphore_signal().
379 // Fall through and drain the wakeup.
380 case DISPATCH_TIME_FOREVER:
381 #if USE_MACH_SEM
382 do {
383 kr = semaphore_wait(dsema->dsema_port);
384 } while (kr == KERN_ABORTED);
385 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
386 #elif USE_POSIX_SEM
387 do {
388 ret = sem_wait(&dsema->dsema_sem);
389 } while (ret != 0);
390 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
391 #elif USE_WIN32_SEM
392 WaitForSingleObject(dsema->dsema_handle, INFINITE);
393 #endif
394 break;
395 }
396 #if USE_MACH_SEM || USE_POSIX_SEM
397 goto again;
398 #else
399 return 0;
400 #endif
401 }
402
403 long
404 dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)
405 {
406 long value = dispatch_atomic_dec2o(dsema, dsema_value, acquire);
407 if (fastpath(value >= 0)) {
408 return 0;
409 }
410 return _dispatch_semaphore_wait_slow(dsema, timeout);
411 }
412
413 #pragma mark -
414 #pragma mark dispatch_group_t
415
416 DISPATCH_ALWAYS_INLINE
417 static inline dispatch_group_t
418 _dispatch_group_create_with_count(long count)
419 {
420 dispatch_group_t dg = (dispatch_group_t)_dispatch_alloc(
421 DISPATCH_VTABLE(group), sizeof(struct dispatch_semaphore_s));
422 _dispatch_semaphore_init(LONG_MAX - count, dg);
423 if (count) {
424 dispatch_atomic_store2o((dispatch_semaphore_t)dg, do_ref_cnt, 1,
425 relaxed); // <rdar://problem/22318411>
426 }
427 return dg;
428 }
429
430 dispatch_group_t
431 dispatch_group_create(void)
432 {
433 return _dispatch_group_create_with_count(0);
434 }
435
436 dispatch_group_t
437 _dispatch_group_create_and_enter(void)
438 {
439 return _dispatch_group_create_with_count(1);
440 }
441
442 void
443 dispatch_group_enter(dispatch_group_t dg)
444 {
445 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
446 long value = dispatch_atomic_dec_orig2o(dsema, dsema_value, acquire);
447 if (value == LONG_MAX) {
448 return _dispatch_retain(dg); // <rdar://problem/22318411>
449 }
450 if (slowpath(value <= 0)) {
451 DISPATCH_CLIENT_CRASH(
452 "Too many nested calls to dispatch_group_enter()");
453 }
454 }
455
456 DISPATCH_NOINLINE
457 static long
458 _dispatch_group_wake(dispatch_semaphore_t dsema, bool needs_release)
459 {
460 dispatch_continuation_t next, head, tail = NULL, dc;
461 long rval;
462
463 head = dispatch_atomic_xchg2o(dsema, dsema_notify_head, NULL, relaxed);
464 if (head) {
465 // snapshot before anything is notified/woken <rdar://problem/8554546>
466 tail = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, NULL, relaxed);
467 }
468 rval = (long)dispatch_atomic_xchg2o(dsema, dsema_group_waiters, 0, relaxed);
469 if (rval) {
470 // wake group waiters
471 #if USE_MACH_SEM
472 _dispatch_semaphore_create_port(&dsema->dsema_port);
473 do {
474 kern_return_t kr = semaphore_signal(dsema->dsema_port);
475 DISPATCH_GROUP_VERIFY_KR(kr);
476 } while (--rval);
477 #elif USE_POSIX_SEM
478 do {
479 int ret = sem_post(&dsema->dsema_sem);
480 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
481 } while (--rval);
482 #elif USE_WIN32_SEM
483 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
484 int ret;
485 ret = ReleaseSemaphore(dsema->dsema_handle, rval, NULL);
486 dispatch_assume(ret);
487 #else
488 #error "No supported semaphore type"
489 #endif
490 }
491 if (head) {
492 // async group notify blocks
493 do {
494 next = fastpath(head->do_next);
495 if (!next && head != tail) {
496 _dispatch_wait_until(next = fastpath(head->do_next));
497 }
498 dispatch_queue_t dsn_queue = (dispatch_queue_t)head->dc_data;
499 dc = _dispatch_continuation_free_cacheonly(head);
500 dispatch_async_f(dsn_queue, head->dc_ctxt, head->dc_func);
501 _dispatch_release(dsn_queue);
502 if (slowpath(dc)) {
503 _dispatch_continuation_free_to_cache_limit(dc);
504 }
505 } while ((head = next));
506 _dispatch_release(dsema);
507 }
508 if (needs_release) {
509 _dispatch_release(dsema); // <rdar://problem/22318411>
510 }
511 return 0;
512 }
513
514 void
515 dispatch_group_leave(dispatch_group_t dg)
516 {
517 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
518 long value = dispatch_atomic_inc2o(dsema, dsema_value, release);
519 if (slowpath(value < 0)) {
520 DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave()");
521 }
522 if (slowpath(value == LONG_MAX)) {
523 return (void)_dispatch_group_wake(dsema, true);
524 }
525 }
526
527 DISPATCH_NOINLINE
528 static long
529 _dispatch_group_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout)
530 {
531 long orig, value;
532
533 #if USE_MACH_SEM
534 mach_timespec_t _timeout;
535 kern_return_t kr;
536 #elif USE_POSIX_SEM // KVV
537 struct timespec _timeout;
538 int ret;
539 #elif USE_WIN32_SEM // KVV
540 uint64_t nsec;
541 DWORD msec;
542 DWORD resolution;
543 DWORD wait_result;
544 #endif
545
546 again:
547 // check before we cause another signal to be sent by incrementing
548 // dsema->dsema_group_waiters
549 value = dispatch_atomic_load2o(dsema, dsema_value, seq_cst); // 19296565
550 if (value == LONG_MAX) {
551 return _dispatch_group_wake(dsema, false);
552 }
553 // Mach semaphores appear to sometimes spuriously wake up. Therefore,
554 // we keep a parallel count of the number of times a Mach semaphore is
555 // signaled (6880961).
556 (void)dispatch_atomic_inc2o(dsema, dsema_group_waiters, relaxed);
557 // check the values again in case we need to wake any threads
558 value = dispatch_atomic_load2o(dsema, dsema_value, seq_cst); // 19296565
559 if (value == LONG_MAX) {
560 return _dispatch_group_wake(dsema, false);
561 }
562
563 #if USE_MACH_SEM
564 _dispatch_semaphore_create_port(&dsema->dsema_port);
565 #elif USE_WIN32_SEM
566 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
567 #endif
568
569 // From xnu/osfmk/kern/sync_sema.c:
570 // wait_semaphore->count = -1; /* we don't keep an actual count */
571 //
572 // The code above does not match the documentation, and that fact is
573 // not surprising. The documented semantics are clumsy to use in any
574 // practical way. The above hack effectively tricks the rest of the
575 // Mach semaphore logic to behave like the libdispatch algorithm.
576
577 switch (timeout) {
578 default:
579 #if USE_MACH_SEM
580 do {
581 uint64_t nsec = _dispatch_timeout(timeout);
582 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
583 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
584 kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout));
585 } while (kr == KERN_ABORTED);
586
587 if (kr != KERN_OPERATION_TIMED_OUT) {
588 DISPATCH_GROUP_VERIFY_KR(kr);
589 break;
590 }
591 #elif USE_POSIX_SEM
592 do {
593 uint64_t nsec = _dispatch_timeout(timeout);
594 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
595 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
596 ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout));
597 } while (ret == -1 && errno == EINTR);
598
599 if (!(ret == -1 && errno == ETIMEDOUT)) {
600 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
601 break;
602 }
603 #elif USE_WIN32_SEM
604 nsec = _dispatch_timeout(timeout);
605 msec = (DWORD)(nsec / (uint64_t)1000000);
606 resolution = _push_timer_resolution(msec);
607 wait_result = WaitForSingleObject(dsema->dsema_handle, msec);
608 _pop_timer_resolution(resolution);
609 if (wait_result != WAIT_TIMEOUT) {
610 break;
611 }
612 #endif
613 // Fall through and try to undo the earlier change to
614 // dsema->dsema_group_waiters
615 case DISPATCH_TIME_NOW:
616 orig = dsema->dsema_group_waiters;
617 while (orig) {
618 if (dispatch_atomic_cmpxchgvw2o(dsema, dsema_group_waiters, orig,
619 orig - 1, &orig, relaxed)) {
620 #if USE_MACH_SEM
621 return KERN_OPERATION_TIMED_OUT;
622 #elif USE_POSIX_SEM || USE_WIN32_SEM
623 errno = ETIMEDOUT;
624 return -1;
625 #endif
626 }
627 }
628 // Another thread called semaphore_signal().
629 // Fall through and drain the wakeup.
630 case DISPATCH_TIME_FOREVER:
631 #if USE_MACH_SEM
632 do {
633 kr = semaphore_wait(dsema->dsema_port);
634 } while (kr == KERN_ABORTED);
635 DISPATCH_GROUP_VERIFY_KR(kr);
636 #elif USE_POSIX_SEM
637 do {
638 ret = sem_wait(&dsema->dsema_sem);
639 } while (ret == -1 && errno == EINTR);
640 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
641 #elif USE_WIN32_SEM
642 WaitForSingleObject(dsema->dsema_handle, INFINITE);
643 #endif
644 break;
645 }
646 goto again;
647 }
648
649 long
650 dispatch_group_wait(dispatch_group_t dg, dispatch_time_t timeout)
651 {
652 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
653
654 if (dsema->dsema_value == LONG_MAX) {
655 return 0;
656 }
657 if (timeout == 0) {
658 #if USE_MACH_SEM
659 return KERN_OPERATION_TIMED_OUT;
660 #elif USE_POSIX_SEM || USE_WIN32_SEM
661 errno = ETIMEDOUT;
662 return (-1);
663 #endif
664 }
665 return _dispatch_group_wait_slow(dsema, timeout);
666 }
667
668 DISPATCH_NOINLINE
669 void
670 dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt,
671 void (*func)(void *))
672 {
673 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
674 dispatch_continuation_t prev, dsn = _dispatch_continuation_alloc();
675 dsn->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT;
676 dsn->dc_data = dq;
677 dsn->dc_ctxt = ctxt;
678 dsn->dc_func = func;
679 dsn->do_next = NULL;
680 _dispatch_retain(dq);
681 prev = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, dsn, release);
682 if (fastpath(prev)) {
683 prev->do_next = dsn;
684 } else {
685 _dispatch_retain(dg);
686 dispatch_atomic_store2o(dsema, dsema_notify_head, dsn, seq_cst);
687 // seq_cst with atomic store to notify_head <rdar://problem/11750916>
688 if (dispatch_atomic_load2o(dsema, dsema_value, seq_cst) == LONG_MAX) {
689 _dispatch_group_wake(dsema, false);
690 }
691 }
692 }
693
694 #ifdef __BLOCKS__
695 void
696 dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
697 dispatch_block_t db)
698 {
699 dispatch_group_notify_f(dg, dq, _dispatch_Block_copy(db),
700 _dispatch_call_block_and_release);
701 }
702 #endif
703
704 #pragma mark -
705 #pragma mark _dispatch_thread_semaphore_t
706
707 _dispatch_thread_semaphore_t
708 _dispatch_thread_semaphore_create(void)
709 {
710 _dispatch_safe_fork = false;
711 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
712 return _os_semaphore_create();
713 #elif USE_MACH_SEM
714 semaphore_t s4;
715 kern_return_t kr;
716 while (slowpath(kr = semaphore_create(mach_task_self(), &s4,
717 SYNC_POLICY_FIFO, 0))) {
718 DISPATCH_VERIFY_MIG(kr);
719 _dispatch_temporary_resource_shortage();
720 }
721 return s4;
722 #elif USE_POSIX_SEM
723 sem_t s4;
724 int ret = sem_init(&s4, 0, 0);
725 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
726 return s4;
727 #elif USE_WIN32_SEM
728 HANDLE tmp;
729 while (!dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL))) {
730 _dispatch_temporary_resource_shortage();
731 }
732 return (_dispatch_thread_semaphore_t)tmp;
733 #else
734 #error "No supported semaphore type"
735 #endif
736 }
737
738 void
739 _dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema)
740 {
741 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
742 return _os_semaphore_dispose(sema);
743 #elif USE_MACH_SEM
744 semaphore_t s4 = (semaphore_t)sema;
745 kern_return_t kr = semaphore_destroy(mach_task_self(), s4);
746 DISPATCH_VERIFY_MIG(kr);
747 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
748 #elif USE_POSIX_SEM
749 sem_t s4 = (sem_t)sema;
750 int ret = sem_destroy(&s4);
751 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
752 #elif USE_WIN32_SEM
753 // XXX: signal the semaphore?
754 WINBOOL success;
755 success = CloseHandle((HANDLE)sema);
756 dispatch_assume(success);
757 #else
758 #error "No supported semaphore type"
759 #endif
760 }
761
762 void
763 _dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema)
764 {
765 // assumed to contain a release barrier
766 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
767 return _os_semaphore_signal(sema);
768 #elif USE_MACH_SEM
769 semaphore_t s4 = (semaphore_t)sema;
770 kern_return_t kr = semaphore_signal(s4);
771 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
772 #elif USE_POSIX_SEM
773 sem_t s4 = (sem_t)sema;
774 int ret = sem_post(&s4);
775 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
776 #elif USE_WIN32_SEM
777 int ret;
778 ret = ReleaseSemaphore((HANDLE)sema, 1, NULL);
779 dispatch_assume(ret);
780 #else
781 #error "No supported semaphore type"
782 #endif
783 }
784
785 void
786 _dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema)
787 {
788 // assumed to contain an acquire barrier
789 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
790 return _os_semaphore_wait(sema);
791 #elif USE_MACH_SEM
792 semaphore_t s4 = (semaphore_t)sema;
793 kern_return_t kr;
794 do {
795 kr = semaphore_wait(s4);
796 } while (slowpath(kr == KERN_ABORTED));
797 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
798 #elif USE_POSIX_SEM
799 sem_t s4 = (sem_t)sema;
800 int ret;
801 do {
802 ret = sem_wait(&s4);
803 } while (slowpath(ret != 0));
804 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
805 #elif USE_WIN32_SEM
806 DWORD wait_result;
807 do {
808 wait_result = WaitForSingleObject((HANDLE)sema, INFINITE);
809 } while (wait_result != WAIT_OBJECT_0);
810 #else
811 #error "No supported semaphore type"
812 #endif
813 }