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 49static DWORD best_resolution = 1; // 1ms 50 51DWORD 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 83void 84_pop_timer_resolution(DWORD ms) 85{ 86 if (ms) { 87 timeEndPeriod(ms); 88 } 89} 90#endif /* USE_WIN32_SEM */ 91 92 93DISPATCH_WEAK // rdar://problem/8503746 94long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema); 95 96static long _dispatch_group_wake(dispatch_semaphore_t dsema); 97 98#pragma mark - 99#pragma mark dispatch_semaphore_t 100 101static 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 117dispatch_semaphore_t 118dispatch_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 138static 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 169static 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 190void 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 218size_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 236DISPATCH_NOINLINE 237long 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 268long 269dispatch_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 281DISPATCH_NOINLINE 282static 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 302again: 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 405long 406dispatch_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 418dispatch_group_t 419dispatch_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 427void 428dispatch_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 438DISPATCH_NOINLINE 439static 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 493void 494dispatch_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 506DISPATCH_NOINLINE 507static 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 525again: 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 626long 627dispatch_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 645DISPATCH_NOINLINE 646void 647dispatch_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__ 672void 673dispatch_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 715void 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 739void 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 762void 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} 791