2 * Copyright (c) 2008-2009 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
24 #define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \
26 DISPATCH_CRASH("flawed group/semaphore logic"); \
30 struct dispatch_semaphore_vtable_s
{
31 DISPATCH_VTABLE_HEADER(dispatch_semaphore_s
);
34 static void _dispatch_semaphore_dispose(dispatch_semaphore_t dsema
);
35 static size_t _dispatch_semaphore_debug(dispatch_semaphore_t dsema
, char *buf
, size_t bufsiz
);
36 static long _dispatch_group_wake(dispatch_semaphore_t dsema
);
38 const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable
= {
39 .do_type
= DISPATCH_SEMAPHORE_TYPE
,
40 .do_kind
= "semaphore",
41 .do_dispose
= _dispatch_semaphore_dispose
,
42 .do_debug
= _dispatch_semaphore_debug
,
46 _dispatch_get_thread_semaphore(void)
48 dispatch_semaphore_t dsema
;
50 dsema
= fastpath(_dispatch_thread_getspecific(dispatch_sema4_key
));
52 while (!(dsema
= dispatch_semaphore_create(0))) {
56 _dispatch_thread_setspecific(dispatch_sema4_key
, NULL
);
61 _dispatch_put_thread_semaphore(dispatch_semaphore_t dsema
)
63 dispatch_semaphore_t old_sema
= _dispatch_thread_getspecific(dispatch_sema4_key
);
64 _dispatch_thread_setspecific(dispatch_sema4_key
, dsema
);
66 dispatch_release(old_sema
);
71 dispatch_group_create(void)
73 return (dispatch_group_t
)dispatch_semaphore_create(LONG_MAX
);
77 dispatch_semaphore_create(long value
)
79 dispatch_semaphore_t dsema
;
81 // If the internal value is negative, then the absolute of the value is
82 // equal to the number of waiting threads. Therefore it is bogus to
83 // initialize the semaphore with a negative value.
88 dsema
= calloc(1, sizeof(struct dispatch_semaphore_s
));
90 if (fastpath(dsema
)) {
91 dsema
->do_vtable
= &_dispatch_semaphore_vtable
;
92 dsema
->do_next
= DISPATCH_OBJECT_LISTLESS
;
93 dsema
->do_ref_cnt
= 1;
94 dsema
->do_xref_cnt
= 1;
95 dsema
->do_targetq
= dispatch_get_global_queue(0, 0);
96 dsema
->dsema_value
= value
;
97 dsema
->dsema_orig
= value
;
104 _dispatch_semaphore_create_port(semaphore_t
*s4
)
113 // lazily allocate the semaphore port
116 // 1) Switch to a doubly-linked FIFO in user-space.
117 // 2) User-space timers for the timeout.
118 // 3) Use the per-thread semaphore port.
120 while (dispatch_assume_zero(kr
= semaphore_create(mach_task_self(), &tmp
, SYNC_POLICY_FIFO
, 0))) {
121 DISPATCH_VERIFY_MIG(kr
);
125 if (!dispatch_atomic_cmpxchg(s4
, 0, tmp
)) {
126 kr
= semaphore_destroy(mach_task_self(), tmp
);
127 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
130 _dispatch_safe_fork
= false;
135 _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema
, dispatch_time_t timeout
)
137 mach_timespec_t _timeout
;
143 // Mach semaphores appear to sometimes spuriously wake up. Therefore,
144 // we keep a parallel count of the number of times a Mach semaphore is
146 while ((orig
= dsema
->dsema_sent_ksignals
)) {
147 if (dispatch_atomic_cmpxchg(&dsema
->dsema_sent_ksignals
, orig
, orig
- 1)) {
152 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
154 // From xnu/osfmk/kern/sync_sema.c:
155 // wait_semaphore->count = -1; /* we don't keep an actual count */
157 // The code above does not match the documentation, and that fact is
158 // not surprising. The documented semantics are clumsy to use in any
159 // practical way. The above hack effectively tricks the rest of the
160 // Mach semaphore logic to behave like the libdispatch algorithm.
165 // timeout() already calculates relative time left
166 nsec
= _dispatch_timeout(timeout
);
167 _timeout
.tv_sec
= (typeof(_timeout
.tv_sec
))(nsec
/ NSEC_PER_SEC
);
168 _timeout
.tv_nsec
= (typeof(_timeout
.tv_nsec
))(nsec
% NSEC_PER_SEC
);
169 kr
= slowpath(semaphore_timedwait(dsema
->dsema_port
, _timeout
));
170 } while (kr
== KERN_ABORTED
);
172 if (kr
!= KERN_OPERATION_TIMED_OUT
) {
173 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
176 // Fall through and try to undo what the fast path did to dsema->dsema_value
177 case DISPATCH_TIME_NOW
:
178 while ((orig
= dsema
->dsema_value
) < 0) {
179 if (dispatch_atomic_cmpxchg(&dsema
->dsema_value
, orig
, orig
+ 1)) {
180 return KERN_OPERATION_TIMED_OUT
;
183 // Another thread called semaphore_signal().
184 // Fall through and drain the wakeup.
185 case DISPATCH_TIME_FOREVER
:
187 kr
= semaphore_wait(dsema
->dsema_port
);
188 } while (kr
== KERN_ABORTED
);
189 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
198 dispatch_group_enter(dispatch_group_t dg
)
200 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)dg
;
201 #if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
203 // 1) Way too much about the optimizer of GCC.
204 // 2) There will never be more than LONG_MAX threads.
205 // Therefore: no overflow detection
213 "xor %%eax, %%eax\n\t"
216 : "+m" (dsema
->dsema_value
)
220 _dispatch_semaphore_wait_slow(dsema
, DISPATCH_TIME_FOREVER
);
222 dispatch_semaphore_wait(dsema
, DISPATCH_TIME_FOREVER
);
228 dispatch_semaphore_wait(dispatch_semaphore_t dsema
, dispatch_time_t timeout
)
230 #if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
232 // 1) Way too much about the optimizer of GCC.
233 // 2) There will never be more than LONG_MAX threads.
234 // Therefore: no overflow detection
242 "xor %%eax, %%eax\n\t"
245 : "+m" (dsema
->dsema_value
)
250 if (dispatch_atomic_dec(&dsema
->dsema_value
) >= 0) {
254 return _dispatch_semaphore_wait_slow(dsema
, timeout
);
259 _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema
)
263 _dispatch_semaphore_create_port(&dsema
->dsema_port
);
265 // Before dsema_sent_ksignals is incremented we can rely on the reference
266 // held by the waiter. However, once this value is incremented the waiter
267 // may return between the atomic increment and the semaphore_signal(),
268 // therefore an explicit reference must be held in order to safely access
269 // dsema after the atomic increment.
270 _dispatch_retain(dsema
);
272 dispatch_atomic_inc(&dsema
->dsema_sent_ksignals
);
274 kr
= semaphore_signal(dsema
->dsema_port
);
275 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
277 _dispatch_release(dsema
);
283 dispatch_group_leave(dispatch_group_t dg
)
285 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)dg
;
287 dispatch_semaphore_signal(dsema
);
289 if (dsema
->dsema_value
== dsema
->dsema_orig
) {
290 _dispatch_group_wake(dsema
);
296 dispatch_semaphore_signal(dispatch_semaphore_t dsema
)
298 #if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
299 // overflow detection
300 // this assumes way too much about the optimizer of GCC
309 "xor %%eax, %%eax\n\t"
314 : "+m" (dsema
->dsema_value
)
319 if (dispatch_atomic_inc(&dsema
->dsema_value
) > 0) {
323 return _dispatch_semaphore_signal_slow(dsema
);
328 _dispatch_group_wake(dispatch_semaphore_t dsema
)
330 struct dispatch_sema_notify_s
*tmp
, *head
= dispatch_atomic_xchg(&dsema
->dsema_notify_head
, NULL
);
331 long rval
= dispatch_atomic_xchg(&dsema
->dsema_group_waiters
, 0);
335 // wake any "group" waiter or notify blocks
338 _dispatch_semaphore_create_port(&dsema
->dsema_waiter_port
);
340 kr
= semaphore_signal(dsema
->dsema_waiter_port
);
341 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
345 dispatch_async_f(head
->dsn_queue
, head
->dsn_ctxt
, head
->dsn_func
);
346 _dispatch_release(head
->dsn_queue
);
348 tmp
= head
->dsn_next
;
349 } while (!tmp
&& !dispatch_atomic_cmpxchg(&dsema
->dsema_notify_tail
, head
, NULL
));
354 _dispatch_release(dsema
);
361 _dispatch_group_wait_slow(dispatch_semaphore_t dsema
, dispatch_time_t timeout
)
363 mach_timespec_t _timeout
;
369 // check before we cause another signal to be sent by incrementing dsema->dsema_group_waiters
370 if (dsema
->dsema_value
== dsema
->dsema_orig
) {
371 return _dispatch_group_wake(dsema
);
373 // Mach semaphores appear to sometimes spuriously wake up. Therefore,
374 // we keep a parallel count of the number of times a Mach semaphore is
376 dispatch_atomic_inc(&dsema
->dsema_group_waiters
);
377 // check the values again in case we need to wake any threads
378 if (dsema
->dsema_value
== dsema
->dsema_orig
) {
379 return _dispatch_group_wake(dsema
);
382 _dispatch_semaphore_create_port(&dsema
->dsema_waiter_port
);
384 // From xnu/osfmk/kern/sync_sema.c:
385 // wait_semaphore->count = -1; /* we don't keep an actual count */
387 // The code above does not match the documentation, and that fact is
388 // not surprising. The documented semantics are clumsy to use in any
389 // practical way. The above hack effectively tricks the rest of the
390 // Mach semaphore logic to behave like the libdispatch algorithm.
395 nsec
= _dispatch_timeout(timeout
);
396 _timeout
.tv_sec
= (typeof(_timeout
.tv_sec
))(nsec
/ NSEC_PER_SEC
);
397 _timeout
.tv_nsec
= (typeof(_timeout
.tv_nsec
))(nsec
% NSEC_PER_SEC
);
398 kr
= slowpath(semaphore_timedwait(dsema
->dsema_waiter_port
, _timeout
));
399 } while (kr
== KERN_ABORTED
);
400 if (kr
!= KERN_OPERATION_TIMED_OUT
) {
401 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
404 // Fall through and try to undo the earlier change to dsema->dsema_group_waiters
405 case DISPATCH_TIME_NOW
:
406 while ((orig
= dsema
->dsema_group_waiters
)) {
407 if (dispatch_atomic_cmpxchg(&dsema
->dsema_group_waiters
, orig
, orig
- 1)) {
408 return KERN_OPERATION_TIMED_OUT
;
411 // Another thread called semaphore_signal().
412 // Fall through and drain the wakeup.
413 case DISPATCH_TIME_FOREVER
:
415 kr
= semaphore_wait(dsema
->dsema_waiter_port
);
416 } while (kr
== KERN_ABORTED
);
417 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
425 dispatch_group_wait(dispatch_group_t dg
, dispatch_time_t timeout
)
427 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)dg
;
429 if (dsema
->dsema_value
== dsema
->dsema_orig
) {
433 return KERN_OPERATION_TIMED_OUT
;
435 return _dispatch_group_wait_slow(dsema
, timeout
);
440 dispatch_group_notify(dispatch_group_t dg
, dispatch_queue_t dq
, dispatch_block_t db
)
442 dispatch_group_notify_f(dg
, dq
, _dispatch_Block_copy(db
), _dispatch_call_block_and_release
);
447 dispatch_group_notify_f(dispatch_group_t dg
, dispatch_queue_t dq
, void *ctxt
, void (*func
)(void *))
449 dispatch_semaphore_t dsema
= (dispatch_semaphore_t
)dg
;
450 struct dispatch_sema_notify_s
*dsn
, *prev
;
452 // FIXME -- this should be updated to use the continuation cache
453 while (!(dsn
= malloc(sizeof(*dsn
)))) {
457 dsn
->dsn_next
= NULL
;
459 dsn
->dsn_ctxt
= ctxt
;
460 dsn
->dsn_func
= func
;
461 _dispatch_retain(dq
);
463 prev
= dispatch_atomic_xchg(&dsema
->dsema_notify_tail
, dsn
);
464 if (fastpath(prev
)) {
465 prev
->dsn_next
= dsn
;
467 _dispatch_retain(dg
);
468 dsema
->dsema_notify_head
= dsn
;
469 if (dsema
->dsema_value
== dsema
->dsema_orig
) {
470 _dispatch_group_wake(dsema
);
476 _dispatch_semaphore_dispose(dispatch_semaphore_t dsema
)
480 if (dsema
->dsema_value
< dsema
->dsema_orig
) {
481 DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use");
484 if (dsema
->dsema_port
) {
485 kr
= semaphore_destroy(mach_task_self(), dsema
->dsema_port
);
486 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
488 if (dsema
->dsema_waiter_port
) {
489 kr
= semaphore_destroy(mach_task_self(), dsema
->dsema_waiter_port
);
490 DISPATCH_SEMAPHORE_VERIFY_KR(kr
);
493 _dispatch_dispose(dsema
);
497 _dispatch_semaphore_debug(dispatch_semaphore_t dsema
, char *buf
, size_t bufsiz
)
500 offset
+= snprintf(&buf
[offset
], bufsiz
- offset
, "%s[%p] = { ", dx_kind(dsema
), dsema
);
501 offset
+= dispatch_object_debug_attr(dsema
, &buf
[offset
], bufsiz
- offset
);
502 offset
+= snprintf(&buf
[offset
], bufsiz
- offset
, "port = 0x%u, value = %ld, orig = %ld }",
503 dsema
->dsema_port
, dsema
->dsema_value
, dsema
->dsema_orig
);
509 dispatch_group_async(dispatch_group_t dg
, dispatch_queue_t dq
, dispatch_block_t db
)
511 dispatch_group_async_f(dg
, dq
, _dispatch_Block_copy(db
), _dispatch_call_block_and_release
);
517 dispatch_group_async_f(dispatch_group_t dg
, dispatch_queue_t dq
, void *ctxt
, void (*func
)(void *))
519 dispatch_continuation_t dc
;
521 _dispatch_retain(dg
);
522 dispatch_group_enter(dg
);
524 dc
= _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap();
526 dc
->do_vtable
= (void *)(DISPATCH_OBJ_ASYNC_BIT
|DISPATCH_OBJ_GROUP_BIT
);
531 _dispatch_queue_push(dq
, dc
);