xref: /trueos/lib/libdispatch/src/semaphore.c (revision 3be304f06dd39a07bcda5ca03a53a3604eb79d2c)
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
_push_timer_resolution(DWORD ms)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
_pop_timer_resolution(DWORD ms)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
_dispatch_semaphore_init(long value,dispatch_object_t dou)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
dispatch_semaphore_create(long value)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
_dispatch_semaphore_create_port(semaphore_t * s4)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
_dispatch_semaphore_create_handle(HANDLE * s4)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
_dispatch_semaphore_dispose(dispatch_object_t dou)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
_dispatch_semaphore_debug(dispatch_object_t dou,char * buf,size_t bufsiz)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
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)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
dispatch_semaphore_signal(dispatch_semaphore_t dsema)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
_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema,dispatch_time_t timeout)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
dispatch_semaphore_wait(dispatch_semaphore_t dsema,dispatch_time_t timeout)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
dispatch_group_create(void)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
dispatch_group_enter(dispatch_group_t dg)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
_dispatch_group_wake(dispatch_semaphore_t dsema)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
dispatch_group_leave(dispatch_group_t dg)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
_dispatch_group_wait_slow(dispatch_semaphore_t dsema,dispatch_time_t timeout)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
dispatch_group_wait(dispatch_group_t dg,dispatch_time_t timeout)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
dispatch_group_notify_f(dispatch_group_t dg,dispatch_queue_t dq,void * ctxt,void (* func)(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
dispatch_group_notify(dispatch_group_t dg,dispatch_queue_t dq,dispatch_block_t db)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
_dispatch_thread_semaphore_create(void)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 	if ((s4 = malloc(sizeof(*s4))) == NULL)
702 		return (0);
703 	int ret = sem_init(s4, 0, 0);
704 	DISPATCH_SEMAPHORE_VERIFY_RET(ret);
705 	return (_dispatch_thread_semaphore_t)s4;
706 #elif USE_WIN32_SEM
707 	HANDLE tmp;
708 	while (!dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL))) {
709 		_dispatch_temporary_resource_shortage();
710 	}
711 	return (_dispatch_thread_semaphore_t)tmp;
712 #else
713 #error "No supported semaphore type"
714 #endif
715 }
716 
717 void
_dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema)718 _dispatch_thread_semaphore_dispose(_dispatch_thread_semaphore_t sema)
719 {
720 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
721 	return _os_semaphore_dispose(sema);
722 #elif USE_MACH_SEM
723 	semaphore_t s4 = (semaphore_t)sema;
724 	kern_return_t kr = semaphore_destroy(mach_task_self(), s4);
725 	DISPATCH_VERIFY_MIG(kr);
726 	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
727 #elif USE_POSIX_SEM
728 	sem_t *s4 = (sem_t *)sema;
729 	int ret = sem_destroy(s4);
730 	DISPATCH_SEMAPHORE_VERIFY_RET(ret);
731 #elif USE_WIN32_SEM
732 	// XXX: signal the semaphore?
733 	WINBOOL success;
734 	success = CloseHandle((HANDLE)sema);
735 	dispatch_assume(success);
736 #else
737 #error "No supported semaphore type"
738 #endif
739 }
740 
741 void
_dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema)742 _dispatch_thread_semaphore_signal(_dispatch_thread_semaphore_t sema)
743 {
744 	// assumed to contain a release barrier
745 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
746 	return _os_semaphore_signal(sema);
747 #elif USE_MACH_SEM
748 	semaphore_t s4 = (semaphore_t)sema;
749 	kern_return_t kr = semaphore_signal(s4);
750 	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
751 #elif USE_POSIX_SEM
752 	sem_t *s4 = (sem_t *)sema;
753 	int ret = sem_post(s4);
754 	DISPATCH_SEMAPHORE_VERIFY_RET(ret);
755 #elif USE_WIN32_SEM
756 	int ret;
757 	ret = ReleaseSemaphore((HANDLE)sema, 1, NULL);
758 	dispatch_assume(ret);
759 #else
760 #error "No supported semaphore type"
761 #endif
762 }
763 
764 void
_dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema)765 _dispatch_thread_semaphore_wait(_dispatch_thread_semaphore_t sema)
766 {
767 	// assumed to contain an acquire barrier
768 #if DISPATCH_USE_OS_SEMAPHORE_CACHE
769 	return _os_semaphore_wait(sema);
770 #elif USE_MACH_SEM
771 	semaphore_t s4 = (semaphore_t)sema;
772 	kern_return_t kr;
773 	do {
774 		kr = semaphore_wait(s4);
775 	} while (slowpath(kr == KERN_ABORTED));
776 	DISPATCH_SEMAPHORE_VERIFY_KR(kr);
777 #elif USE_POSIX_SEM
778 	sem_t *s4 = (sem_t *)sema;
779 	int ret;
780 	do {
781 		ret = sem_wait(s4);
782 	} while (slowpath(ret != 0));
783 	DISPATCH_SEMAPHORE_VERIFY_RET(ret);
784 #elif USE_WIN32_SEM
785 	DWORD wait_result;
786 	do {
787 		wait_result = WaitForSingleObject((HANDLE)sema, INFINITE);
788 	} while (wait_result != WAIT_OBJECT_0);
789 #else
790 #error "No supported semaphore type"
791 #endif
792 }
793