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