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