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