]> git.saurik.com Git - apple/libdispatch.git/blob - src/semaphore.c
libdispatch-442.1.4.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_group_t
419 dispatch_group_create(void)
420 {
421 dispatch_group_t dg = (dispatch_group_t)_dispatch_alloc(
422 DISPATCH_VTABLE(group), sizeof(struct dispatch_semaphore_s));
423 _dispatch_semaphore_init(LONG_MAX, dg);
424 return dg;
425 }
426
427 void
428 dispatch_group_enter(dispatch_group_t dg)
429 {
430 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
431 long value = dispatch_atomic_dec2o(dsema, dsema_value, acquire);
432 if (slowpath(value < 0)) {
433 DISPATCH_CLIENT_CRASH(
434 "Too many nested calls to dispatch_group_enter()");
435 }
436 }
437
438 DISPATCH_NOINLINE
439 static long
440 _dispatch_group_wake(dispatch_semaphore_t dsema)
441 {
442 dispatch_continuation_t next, head, tail = NULL, dc;
443 long rval;
444
445 head = dispatch_atomic_xchg2o(dsema, dsema_notify_head, NULL, relaxed);
446 if (head) {
447 // snapshot before anything is notified/woken <rdar://problem/8554546>
448 tail = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, NULL, relaxed);
449 }
450 rval = (long)dispatch_atomic_xchg2o(dsema, dsema_group_waiters, 0, relaxed);
451 if (rval) {
452 // wake group waiters
453 #if USE_MACH_SEM
454 _dispatch_semaphore_create_port(&dsema->dsema_port);
455 do {
456 kern_return_t kr = semaphore_signal(dsema->dsema_port);
457 DISPATCH_GROUP_VERIFY_KR(kr);
458 } while (--rval);
459 #elif USE_POSIX_SEM
460 do {
461 int ret = sem_post(&dsema->dsema_sem);
462 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
463 } while (--rval);
464 #elif USE_WIN32_SEM
465 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
466 int ret;
467 ret = ReleaseSemaphore(dsema->dsema_handle, rval, NULL);
468 dispatch_assume(ret);
469 #else
470 #error "No supported semaphore type"
471 #endif
472 }
473 if (head) {
474 // async group notify blocks
475 do {
476 next = fastpath(head->do_next);
477 if (!next && head != tail) {
478 _dispatch_wait_until(next = fastpath(head->do_next));
479 }
480 dispatch_queue_t dsn_queue = (dispatch_queue_t)head->dc_data;
481 dc = _dispatch_continuation_free_cacheonly(head);
482 dispatch_async_f(dsn_queue, head->dc_ctxt, head->dc_func);
483 _dispatch_release(dsn_queue);
484 if (slowpath(dc)) {
485 _dispatch_continuation_free_to_cache_limit(dc);
486 }
487 } while ((head = next));
488 _dispatch_release(dsema);
489 }
490 return 0;
491 }
492
493 void
494 dispatch_group_leave(dispatch_group_t dg)
495 {
496 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
497 long value = dispatch_atomic_inc2o(dsema, dsema_value, release);
498 if (slowpath(value < 0)) {
499 DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_group_leave()");
500 }
501 if (slowpath(value == LONG_MAX)) {
502 (void)_dispatch_group_wake(dsema);
503 }
504 }
505
506 DISPATCH_NOINLINE
507 static long
508 _dispatch_group_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout)
509 {
510 long orig;
511
512 #if USE_MACH_SEM
513 mach_timespec_t _timeout;
514 kern_return_t kr;
515 #elif USE_POSIX_SEM // KVV
516 struct timespec _timeout;
517 int ret;
518 #elif USE_WIN32_SEM // KVV
519 uint64_t nsec;
520 DWORD msec;
521 DWORD resolution;
522 DWORD wait_result;
523 #endif
524
525 again:
526 // check before we cause another signal to be sent by incrementing
527 // dsema->dsema_group_waiters
528 if (dsema->dsema_value == LONG_MAX) {
529 return _dispatch_group_wake(dsema);
530 }
531 // Mach semaphores appear to sometimes spuriously wake up. Therefore,
532 // we keep a parallel count of the number of times a Mach semaphore is
533 // signaled (6880961).
534 (void)dispatch_atomic_inc2o(dsema, dsema_group_waiters, relaxed);
535 // check the values again in case we need to wake any threads
536 if (dsema->dsema_value == LONG_MAX) {
537 return _dispatch_group_wake(dsema);
538 }
539
540 #if USE_MACH_SEM
541 _dispatch_semaphore_create_port(&dsema->dsema_port);
542 #elif USE_WIN32_SEM
543 _dispatch_semaphore_create_handle(&dsema->dsema_handle);
544 #endif
545
546 // From xnu/osfmk/kern/sync_sema.c:
547 // wait_semaphore->count = -1; /* we don't keep an actual count */
548 //
549 // The code above does not match the documentation, and that fact is
550 // not surprising. The documented semantics are clumsy to use in any
551 // practical way. The above hack effectively tricks the rest of the
552 // Mach semaphore logic to behave like the libdispatch algorithm.
553
554 switch (timeout) {
555 default:
556 #if USE_MACH_SEM
557 do {
558 uint64_t nsec = _dispatch_timeout(timeout);
559 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
560 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
561 kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout));
562 } while (kr == KERN_ABORTED);
563
564 if (kr != KERN_OPERATION_TIMED_OUT) {
565 DISPATCH_GROUP_VERIFY_KR(kr);
566 break;
567 }
568 #elif USE_POSIX_SEM
569 do {
570 uint64_t nsec = _dispatch_timeout(timeout);
571 _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC);
572 _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC);
573 ret = slowpath(sem_timedwait(&dsema->dsema_sem, &_timeout));
574 } while (ret == -1 && errno == EINTR);
575
576 if (!(ret == -1 && errno == ETIMEDOUT)) {
577 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
578 break;
579 }
580 #elif USE_WIN32_SEM
581 nsec = _dispatch_timeout(timeout);
582 msec = (DWORD)(nsec / (uint64_t)1000000);
583 resolution = _push_timer_resolution(msec);
584 wait_result = WaitForSingleObject(dsema->dsema_handle, msec);
585 _pop_timer_resolution(resolution);
586 if (wait_result != WAIT_TIMEOUT) {
587 break;
588 }
589 #endif
590 // Fall through and try to undo the earlier change to
591 // dsema->dsema_group_waiters
592 case DISPATCH_TIME_NOW:
593 orig = dsema->dsema_group_waiters;
594 while (orig) {
595 if (dispatch_atomic_cmpxchgvw2o(dsema, dsema_group_waiters, orig,
596 orig - 1, &orig, relaxed)) {
597 #if USE_MACH_SEM
598 return KERN_OPERATION_TIMED_OUT;
599 #elif USE_POSIX_SEM || USE_WIN32_SEM
600 errno = ETIMEDOUT;
601 return -1;
602 #endif
603 }
604 }
605 // Another thread called semaphore_signal().
606 // Fall through and drain the wakeup.
607 case DISPATCH_TIME_FOREVER:
608 #if USE_MACH_SEM
609 do {
610 kr = semaphore_wait(dsema->dsema_port);
611 } while (kr == KERN_ABORTED);
612 DISPATCH_GROUP_VERIFY_KR(kr);
613 #elif USE_POSIX_SEM
614 do {
615 ret = sem_wait(&dsema->dsema_sem);
616 } while (ret == -1 && errno == EINTR);
617 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
618 #elif USE_WIN32_SEM
619 WaitForSingleObject(dsema->dsema_handle, INFINITE);
620 #endif
621 break;
622 }
623 goto again;
624 }
625
626 long
627 dispatch_group_wait(dispatch_group_t dg, dispatch_time_t timeout)
628 {
629 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
630
631 if (dsema->dsema_value == LONG_MAX) {
632 return 0;
633 }
634 if (timeout == 0) {
635 #if USE_MACH_SEM
636 return KERN_OPERATION_TIMED_OUT;
637 #elif USE_POSIX_SEM || USE_WIN32_SEM
638 errno = ETIMEDOUT;
639 return (-1);
640 #endif
641 }
642 return _dispatch_group_wait_slow(dsema, timeout);
643 }
644
645 DISPATCH_NOINLINE
646 void
647 dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt,
648 void (*func)(void *))
649 {
650 dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg;
651 dispatch_continuation_t prev, dsn = _dispatch_continuation_alloc();
652 dsn->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT;
653 dsn->dc_data = dq;
654 dsn->dc_ctxt = ctxt;
655 dsn->dc_func = func;
656 dsn->do_next = NULL;
657 _dispatch_retain(dq);
658 prev = dispatch_atomic_xchg2o(dsema, dsema_notify_tail, dsn, release);
659 if (fastpath(prev)) {
660 prev->do_next = dsn;
661 } else {
662 _dispatch_retain(dg);
663 dispatch_atomic_store2o(dsema, dsema_notify_head, dsn, seq_cst);
664 // seq_cst with atomic store to notify_head <rdar://problem/11750916>
665 if (dispatch_atomic_load2o(dsema, dsema_value, seq_cst) == LONG_MAX) {
666 _dispatch_group_wake(dsema);
667 }
668 }
669 }
670
671 #ifdef __BLOCKS__
672 void
673 dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq,
674 dispatch_block_t db)
675 {
676 dispatch_group_notify_f(dg, dq, _dispatch_Block_copy(db),
677 _dispatch_call_block_and_release);
678 }
679 #endif
680
681 #pragma mark -
682 #pragma mark _dispatch_thread_semaphore_t
683
684 _dispatch_thread_semaphore_t
685 _dispatch_thread_semaphore_create(void)
686 {
687 _dispatch_safe_fork = false;
688 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
689 return _os_semaphore_create();
690 #elif USE_MACH_SEM
691 semaphore_t s4;
692 kern_return_t kr;
693 while (slowpath(kr = semaphore_create(mach_task_self(), &s4,
694 SYNC_POLICY_FIFO, 0))) {
695 DISPATCH_VERIFY_MIG(kr);
696 _dispatch_temporary_resource_shortage();
697 }
698 return s4;
699 #elif USE_POSIX_SEM
700 sem_t s4;
701 int ret = sem_init(&s4, 0, 0);
702 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
703 return s4;
704 #elif USE_WIN32_SEM
705 HANDLE tmp;
706 while (!dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL))) {
707 _dispatch_temporary_resource_shortage();
708 }
709 return (_dispatch_thread_semaphore_t)tmp;
710 #else
711 #error "No supported semaphore type"
712 #endif
713 }
714
715 void
716 _dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema)
717 {
718 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
719 return _os_semaphore_dispose(sema);
720 #elif USE_MACH_SEM
721 semaphore_t s4 = (semaphore_t)sema;
722 kern_return_t kr = semaphore_destroy(mach_task_self(), s4);
723 DISPATCH_VERIFY_MIG(kr);
724 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
725 #elif USE_POSIX_SEM
726 sem_t s4 = (sem_t)sema;
727 int ret = sem_destroy(&s4);
728 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
729 #elif USE_WIN32_SEM
730 // XXX: signal the semaphore?
731 WINBOOL success;
732 success = CloseHandle((HANDLE)sema);
733 dispatch_assume(success);
734 #else
735 #error "No supported semaphore type"
736 #endif
737 }
738
739 void
740 _dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema)
741 {
742 // assumed to contain a release barrier
743 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
744 return _os_semaphore_signal(sema);
745 #elif USE_MACH_SEM
746 semaphore_t s4 = (semaphore_t)sema;
747 kern_return_t kr = semaphore_signal(s4);
748 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
749 #elif USE_POSIX_SEM
750 sem_t s4 = (sem_t)sema;
751 int ret = sem_post(&s4);
752 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
753 #elif USE_WIN32_SEM
754 int ret;
755 ret = ReleaseSemaphore((HANDLE)sema, 1, NULL);
756 dispatch_assume(ret);
757 #else
758 #error "No supported semaphore type"
759 #endif
760 }
761
762 void
763 _dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema)
764 {
765 // assumed to contain an acquire barrier
766 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
767 return _os_semaphore_wait(sema);
768 #elif USE_MACH_SEM
769 semaphore_t s4 = (semaphore_t)sema;
770 kern_return_t kr;
771 do {
772 kr = semaphore_wait(s4);
773 } while (slowpath(kr == KERN_ABORTED));
774 DISPATCH_SEMAPHORE_VERIFY_KR(kr);
775 #elif USE_POSIX_SEM
776 sem_t s4 = (sem_t)sema;
777 int ret;
778 do {
779 ret = sem_wait(&s4);
780 } while (slowpath(ret != 0));
781 DISPATCH_SEMAPHORE_VERIFY_RET(ret);
782 #elif USE_WIN32_SEM
783 DWORD wait_result;
784 do {
785 wait_result = WaitForSingleObject((HANDLE)sema, INFINITE);
786 } while (wait_result != WAIT_OBJECT_0);
787 #else
788 #error "No supported semaphore type"
789 #endif
790 }