2 * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
23 // semaphores are too fundamental to use the dispatch_assume*() macros
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"); \
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"); \
40 #define DISPATCH_SEMAPHORE_VERIFY_RET(x) do { \
41 if (slowpath((x) == -1)) { \
42 DISPATCH_CRASH("POSIX semaphore API failure"); \
48 // rdar://problem/8428132
49 static DWORD best_resolution
= 1; // 1ms
52 _push_timer_resolution(DWORD ms
)
55 static dispatch_once_t once
;
58 // only update timer resolution if smaller than default 15.6ms
59 // zero means not updated
63 // aim for the best resolution we can accomplish
64 dispatch_once(&once
, ^{
67 res
= timeGetDevCaps(&tc
, sizeof(tc
));
68 if (res
== MMSYSERR_NOERROR
) {
69 best_resolution
= min(max(tc
.wPeriodMin
, best_resolution
),
74 res
= timeBeginPeriod(best_resolution
);
75 if (res
== TIMERR_NOERROR
) {
76 return best_resolution
;
78 // zero means not updated
82 // match ms parameter to result from _push_timer_resolution
84 _pop_timer_resolution(DWORD ms
)
90 #endif /* USE_WIN32_SEM */
93 DISPATCH_WEAK
// rdar://problem/8503746
94 long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema
);
96 static long _dispatch_group_wake(dispatch_semaphore_t dsema
);
99 #pragma mark dispatch_semaphore_t
102 _dispatch_semaphore_init(long value
, dispatch_object_t dou
)
104 dispatch_semaphore_t dsema
= dou
._dsema
;
106 dsema
->do_next
= (dispatch_semaphore_t
)DISPATCH_OBJECT_LISTLESS
;
107 dsema
->do_targetq
= _dispatch_get_root_queue(_DISPATCH_QOS_CLASS_DEFAULT
,
109 dsema
->dsema_value
= value
;
110 dsema
->dsema_orig
= value
;
112 int ret
= sem_init(&dsema
->dsema_sem
, 0, 0);
113 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
118 dispatch_semaphore_create(long value
)
120 dispatch_semaphore_t dsema
;
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.
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
);
139 _dispatch_semaphore_create_port(semaphore_t
*s4
)
147 _dispatch_safe_fork
= false;
149 // lazily allocate the semaphore port
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.
156 while ((kr
= semaphore_create(mach_task_self(), &tmp
,
157 SYNC_POLICY_FIFO
, 0))) {
158 DISPATCH_VERIFY_MIG(kr
);
159 _dispatch_temporary_resource_shortage();
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
);
170 _dispatch_semaphore_create_handle(HANDLE
*s4
)
178 // lazily allocate the semaphore port
180 while (!dispatch_assume(tmp
= CreateSemaphore(NULL
, 0, LONG_MAX
, NULL
))) {
181 _dispatch_temporary_resource_shortage();
184 if (!dispatch_atomic_cmpxchg(s4
, 0, tmp
)) {
191 _dispatch_semaphore_dispose(dispatch_object_t dou
)
193 dispatch_semaphore_t dsema
= dou
._dsema
;
195 if (dsema
->dsema_value
< dsema
->dsema_orig
) {
196 DISPATCH_CLIENT_CRASH(
197 "Semaphore/group object deallocated while in use");
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
);
207 dsema
->dsema_port
= MACH_PORT_DEAD
;
209 int ret
= sem_destroy(&dsema
->dsema_sem
);
210 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
212 if (dsema
->dsema_handle
) {
213 CloseHandle(dsema
->dsema_handle
);
219 _dispatch_semaphore_debug(dispatch_object_t dou
, char *buf
, size_t bufsiz
)
221 dispatch_semaphore_t dsema
= dou
._dsema
;
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
);
228 offset
+= dsnprintf(&buf
[offset
], bufsiz
- offset
, "port = 0x%u, ",
231 offset
+= dsnprintf(&buf
[offset
], bufsiz
- offset
,
232 "value = %ld, orig = %ld }", dsema
->dsema_value
, dsema
->dsema_orig
);
238 _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema
)
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
);
247 #if USE_MACH_SEM || USE_POSIX_SEM
248 (void)dispatch_atomic_inc2o(dsema
, dsema_sent_ksignals
, relaxed
);
252 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
253 kern_return_t kr
= semaphore_signal(dsema
->dsema_port
);
254 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
256 int ret
= sem_post(&dsema
->dsema_sem
);
257 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
259 _dispatch_semaphore_create_handle(&dsema
->dsema_handle
);
260 int ret
= ReleaseSemaphore(dsema
->dsema_handle
, 1, NULL
);
261 dispatch_assume(ret
);
264 _dispatch_release(dsema
);
269 dispatch_semaphore_signal(dispatch_semaphore_t dsema
)
271 long value
= dispatch_atomic_inc2o(dsema
, dsema_value
, release
);
272 if (fastpath(value
> 0)) {
275 if (slowpath(value
== LONG_MIN
)) {
276 DISPATCH_CLIENT_CRASH("Unbalanced call to dispatch_semaphore_signal()");
278 return _dispatch_semaphore_signal_slow(dsema
);
283 _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema
,
284 dispatch_time_t timeout
)
289 mach_timespec_t _timeout
;
292 struct timespec _timeout
;
301 #if USE_MACH_SEM || USE_POSIX_SEM
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
;
308 if (dispatch_atomic_cmpxchgvw2o(dsema
, dsema_sent_ksignals
, orig
,
309 orig
- 1, &orig
, relaxed
)) {
316 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
318 _dispatch_semaphore_create_handle(&dsema
->dsema_handle
);
321 // From xnu/osfmk/kern/sync_sema.c:
322 // wait_semaphore->count = -1; /* we don't keep an actual count */
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.
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
);
339 if (kr
!= KERN_OPERATION_TIMED_OUT
) {
340 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
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
);
351 if (ret
== -1 && errno
!= ETIMEDOUT
) {
352 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
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
) {
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
;
370 if (dispatch_atomic_cmpxchgvw2o(dsema
, dsema_value
, orig
, orig
+ 1,
373 return KERN_OPERATION_TIMED_OUT
;
374 #elif USE_POSIX_SEM || USE_WIN32_SEM
380 // Another thread called semaphore_signal().
381 // Fall through and drain the wakeup.
382 case DISPATCH_TIME_FOREVER
:
385 kr
= semaphore_wait(dsema
->dsema_port
);
386 } while (kr
== KERN_ABORTED
);
387 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
390 ret
= sem_wait(&dsema
->dsema_sem
);
392 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
394 WaitForSingleObject(dsema
->dsema_handle
, INFINITE
);
398 #if USE_MACH_SEM || USE_POSIX_SEM
406 dispatch_semaphore_wait(dispatch_semaphore_t dsema
, dispatch_time_t timeout
)
408 long value
= dispatch_atomic_dec2o(dsema
, dsema_value
, acquire
);
409 if (fastpath(value
>= 0)) {
412 return _dispatch_semaphore_wait_slow(dsema
, timeout
);
416 #pragma mark dispatch_group_t
419 dispatch_group_create(void)
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
);
428 dispatch_group_enter(dispatch_group_t dg
)
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()");
440 _dispatch_group_wake(dispatch_semaphore_t dsema
)
442 dispatch_continuation_t next
, head
, tail
= NULL
, dc
;
445 head
= dispatch_atomic_xchg2o(dsema
, dsema_notify_head
, NULL
, relaxed
);
447 // snapshot before anything is notified/woken <rdar://problem/8554546>
448 tail
= dispatch_atomic_xchg2o(dsema
, dsema_notify_tail
, NULL
, relaxed
);
450 rval
= (long)dispatch_atomic_xchg2o(dsema
, dsema_group_waiters
, 0, relaxed
);
452 // wake group waiters
454 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
456 kern_return_t kr
= semaphore_signal(dsema
->dsema_port
);
457 DISPATCH_GROUP_VERIFY_KR(kr
);
461 int ret
= sem_post(&dsema
->dsema_sem
);
462 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
465 _dispatch_semaphore_create_handle(&dsema
->dsema_handle
);
467 ret
= ReleaseSemaphore(dsema
->dsema_handle
, rval
, NULL
);
468 dispatch_assume(ret
);
470 #error "No supported semaphore type"
474 // async group notify blocks
476 next
= fastpath(head
->do_next
);
477 if (!next
&& head
!= tail
) {
478 _dispatch_wait_until(next
= fastpath(head
->do_next
));
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
);
485 _dispatch_continuation_free_to_cache_limit(dc
);
487 } while ((head
= next
));
488 _dispatch_release(dsema
);
494 dispatch_group_leave(dispatch_group_t dg
)
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()");
501 if (slowpath(value
== LONG_MAX
)) {
502 (void)_dispatch_group_wake(dsema
);
508 _dispatch_group_wait_slow(dispatch_semaphore_t dsema
, dispatch_time_t timeout
)
513 mach_timespec_t _timeout
;
515 #elif USE_POSIX_SEM // KVV
516 struct timespec _timeout
;
518 #elif USE_WIN32_SEM // KVV
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
);
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
);
541 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
543 _dispatch_semaphore_create_handle(&dsema
->dsema_handle
);
546 // From xnu/osfmk/kern/sync_sema.c:
547 // wait_semaphore->count = -1; /* we don't keep an actual count */
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.
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
);
564 if (kr
!= KERN_OPERATION_TIMED_OUT
) {
565 DISPATCH_GROUP_VERIFY_KR(kr
);
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
);
576 if (!(ret
== -1 && errno
== ETIMEDOUT
)) {
577 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
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
) {
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
;
595 if (dispatch_atomic_cmpxchgvw2o(dsema
, dsema_group_waiters
, orig
,
596 orig
- 1, &orig
, relaxed
)) {
598 return KERN_OPERATION_TIMED_OUT
;
599 #elif USE_POSIX_SEM || USE_WIN32_SEM
605 // Another thread called semaphore_signal().
606 // Fall through and drain the wakeup.
607 case DISPATCH_TIME_FOREVER
:
610 kr
= semaphore_wait(dsema
->dsema_port
);
611 } while (kr
== KERN_ABORTED
);
612 DISPATCH_GROUP_VERIFY_KR(kr
);
615 ret
= sem_wait(&dsema
->dsema_sem
);
616 } while (ret
== -1 && errno
== EINTR
);
617 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
619 WaitForSingleObject(dsema
->dsema_handle
, INFINITE
);
627 dispatch_group_wait(dispatch_group_t dg
, dispatch_time_t timeout
)
629 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)dg
;
631 if (dsema
->dsema_value
== LONG_MAX
) {
636 return KERN_OPERATION_TIMED_OUT
;
637 #elif USE_POSIX_SEM || USE_WIN32_SEM
642 return _dispatch_group_wait_slow(dsema
, timeout
);
647 dispatch_group_notify_f(dispatch_group_t dg
, dispatch_queue_t dq
, void *ctxt
,
648 void (*func
)(void *))
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
;
657 _dispatch_retain(dq
);
658 prev
= dispatch_atomic_xchg2o(dsema
, dsema_notify_tail
, dsn
, release
);
659 if (fastpath(prev
)) {
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
);
673 dispatch_group_notify(dispatch_group_t dg
, dispatch_queue_t dq
,
676 dispatch_group_notify_f(dg
, dq
, _dispatch_Block_copy(db
),
677 _dispatch_call_block_and_release
);
682 #pragma mark _dispatch_thread_semaphore_t
684 _dispatch_thread_semaphore_t
685 _dispatch_thread_semaphore_create(void)
687 _dispatch_safe_fork
= false;
688 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
689 return _os_semaphore_create();
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();
701 int ret
= sem_init(&s4
, 0, 0);
702 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
706 while (!dispatch_assume(tmp
= CreateSemaphore(NULL
, 0, LONG_MAX
, NULL
))) {
707 _dispatch_temporary_resource_shortage();
709 return (_dispatch_thread_semaphore_t
)tmp
;
711 #error "No supported semaphore type"
716 _dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema
)
718 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
719 return _os_semaphore_dispose(sema
);
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
);
726 sem_t s4
= (sem_t
)sema
;
727 int ret
= sem_destroy(&s4
);
728 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
730 // XXX: signal the semaphore?
732 success
= CloseHandle((HANDLE
)sema
);
733 dispatch_assume(success
);
735 #error "No supported semaphore type"
740 _dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema
)
742 // assumed to contain a release barrier
743 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
744 return _os_semaphore_signal(sema
);
746 semaphore_t s4
= (semaphore_t
)sema
;
747 kern_return_t kr
= semaphore_signal(s4
);
748 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
750 sem_t s4
= (sem_t
)sema
;
751 int ret
= sem_post(&s4
);
752 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
755 ret
= ReleaseSemaphore((HANDLE
)sema
, 1, NULL
);
756 dispatch_assume(ret
);
758 #error "No supported semaphore type"
763 _dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema
)
765 // assumed to contain an acquire barrier
766 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
767 return _os_semaphore_wait(sema
);
769 semaphore_t s4
= (semaphore_t
)sema
;
772 kr
= semaphore_wait(s4
);
773 } while (slowpath(kr
== KERN_ABORTED
));
774 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
776 sem_t s4
= (sem_t
)sema
;
780 } while (slowpath(ret
!= 0));
781 DISPATCH_SEMAPHORE_VERIFY_RET(ret
);
785 wait_result
= WaitForSingleObject((HANDLE
)sema
, INFINITE
);
786 } while (wait_result
!= WAIT_OBJECT_0
);
788 #error "No supported semaphore type"