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