Revision: 192 http://trac.macosforge.org/projects/libdispatch/changeset/192 Author: kvv@apple.com Date: 2010-10-24 01:25:52 -0700 (Sun, 24 Oct 2010) Log Message: ----------- Support for time and semaphores on Win32. Modified Paths: -------------- trunk/src/internal.h trunk/src/queue.c trunk/src/semaphore.c trunk/src/semaphore_internal.h trunk/src/shims/time.c trunk/src/shims/time.h trunk/src/time.c Modified: trunk/src/internal.h =================================================================== --- trunk/src/internal.h 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/internal.h 2010-10-24 08:25:52 UTC (rev 192) @@ -29,6 +29,37 @@ #include "config/config.h" +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +#if TARGET_OS_WIN32 +// Include Win32 headers early in order to minimize the +// likelihood of name pollution from dispatch headers. + +#ifndef WINVER +#define WINVER 0x502 +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x502 +#endif + +#ifndef _MSC_VER +#define _MSC_VER 1400 +#pragma warning(disable:4159) +#endif + +#define WIN32_LEAN_AND_MEAN 1 +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_SECURE_NO_WARNINGS 1 + +#define BOOL WINBOOL +#include <Windows.h> +#undef BOOL + +#endif /* TARGET_OS_WIN32 */ + #define __DISPATCH_BUILDING_DISPATCH__ #define __DISPATCH_INDIRECT__ @@ -96,6 +127,9 @@ #include <netinet/in.h> #ifdef __BLOCKS__ +#if TARGET_OS_WIN32 +#define BLOCK_EXPORT extern "C" __declspec(dllexport) +#endif /* TARGET_OS_WIN32 */ #include <Block_private.h> #include <Block.h> #endif /* __BLOCKS__ */ Modified: trunk/src/queue.c =================================================================== --- trunk/src/queue.c 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/queue.c 2010-10-24 08:25:52 UTC (rev 192) @@ -1200,6 +1200,10 @@ ret = sem_init(&_dispatch_thread_mediator[i].dsema_sem, 0, 0); (void)dispatch_assume_zero(ret); #endif +#if USE_WIN32_SEM + _dispatch_thread_mediator[i].dsema_handle = CreateSemaphore(NULL, 0, LONG_MAX, NULL); + dispatch_assume(_dispatch_thread_mediator[i].dsema_handle); +#endif #if HAVE_PTHREAD_WORKQUEUES } else { dispatch_assume(_dispatch_root_queue_contexts[i].dgq_kworkqueue); Modified: trunk/src/semaphore.c =================================================================== --- trunk/src/semaphore.c 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/semaphore.c 2010-10-24 08:25:52 UTC (rev 192) @@ -56,7 +56,7 @@ { dispatch_semaphore_t dsema; - dsema = fastpath(_dispatch_thread_getspecific(dispatch_sema4_key)); + dsema = (dispatch_semaphore_t)fastpath(_dispatch_thread_getspecific(dispatch_sema4_key)); if (!dsema) { while (!(dsema = dispatch_semaphore_create(0))) { sleep(1); @@ -69,7 +69,7 @@ void _dispatch_put_thread_semaphore(dispatch_semaphore_t dsema) { - dispatch_semaphore_t old_sema = _dispatch_thread_getspecific(dispatch_sema4_key); + dispatch_semaphore_t old_sema = (dispatch_semaphore_t)_dispatch_thread_getspecific(dispatch_sema4_key); _dispatch_thread_setspecific(dispatch_sema4_key, dsema); if (old_sema) { dispatch_release(old_sema); @@ -97,11 +97,11 @@ return NULL; } - dsema = calloc(1, sizeof(struct dispatch_semaphore_s)); + dsema = (dispatch_semaphore_t)calloc(1, sizeof(struct dispatch_semaphore_s)); if (fastpath(dsema)) { dsema->do_vtable = &_dispatch_semaphore_vtable; - dsema->do_next = DISPATCH_OBJECT_LISTLESS; + dsema->do_next = (dispatch_semaphore_t)DISPATCH_OBJECT_LISTLESS; dsema->do_ref_cnt = 1; dsema->do_xref_cnt = 1; dsema->do_targetq = dispatch_get_global_queue(0, 0); @@ -148,6 +148,28 @@ } #endif +#if USE_WIN32_SEM +static void +_dispatch_semaphore_create_handle(HANDLE *s4) +{ + HANDLE tmp; + + if (*s4) { + return; + } + + // lazily allocate the semaphore port + + while (dispatch_assume(tmp = CreateSemaphore(NULL, 0, LONG_MAX, NULL)) == NULL) { + sleep(1); + } + + if (!dispatch_atomic_cmpxchg(s4, 0, tmp)) { + CloseHandle(tmp); + } +} +#endif /* USE_WIN32_SEM */ + DISPATCH_NOINLINE static long _dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout) @@ -155,10 +177,11 @@ #if USE_MACH_SEM mach_timespec_t _timeout; kern_return_t kr; - uint64_t nsec; #endif #if USE_POSIX_SEM struct timespec _timeout; +#endif +#if USE_POSIX_SEM || USE_WIN32_SEM int ret; #endif long orig; @@ -176,6 +199,9 @@ #if USE_MACH_SEM _dispatch_semaphore_create_port(&dsema->dsema_port); #endif +#if USE_WIN32_SEM + _dispatch_semaphore_create_handle(&dsema->dsema_handle); +#endif // From xnu/osfmk/kern/sync_sema.c: // wait_semaphore->count = -1; /* we don't keep an actual count */ @@ -189,6 +215,7 @@ default: #if USE_MACH_SEM do { + uint64_t nsec; // timeout() already calculates relative time left nsec = _dispatch_timeout(timeout); _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); @@ -213,6 +240,18 @@ break; } #endif +#if USE_WIN32_SEM + do { + uint64_t nsec; + DWORD msec; + nsec = _dispatch_timeout(timeout); + msec = (DWORD)(nsec / (uint64_t)1000000); + ret = WaitForSingleObject(dsema->dsema_handle, msec); + } while (ret != WAIT_OBJECT_0 && ret != WAIT_TIMEOUT); + if (ret != WAIT_TIMEOUT) { + break; + } +#endif /* USE_WIN32_SEM */ // Fall through and try to undo what the fast path did to dsema->dsema_value case DISPATCH_TIME_NOW: while ((orig = dsema->dsema_value) < 0) { @@ -220,7 +259,7 @@ #if USE_MACH_SEM return KERN_OPERATION_TIMED_OUT; #endif -#if USE_POSIX_SEM +#if USE_POSIX_SEM || USE_WIN32_SEM errno = ETIMEDOUT; return -1; #endif @@ -241,6 +280,11 @@ } while (ret != 0); DISPATCH_SEMAPHORE_VERIFY_RET(ret); #endif +#if USE_WIN32_SEM + do { + ret = WaitForSingleObject(dsema->dsema_handle, INFINITE); + } while (ret != WAIT_OBJECT_0); +#endif break; } @@ -311,7 +355,7 @@ static long _dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) { -#if USE_POSIX_SEM +#if USE_POSIX_SEM || USE_WIN32_SEM int ret; #endif #if USE_MACH_SEM @@ -319,6 +363,9 @@ _dispatch_semaphore_create_port(&dsema->dsema_port); #endif +#if USE_WIN32_SEM + _dispatch_semaphore_create_handle(&dsema->dsema_handle); +#endif // Before dsema_sent_ksignals is incremented we can rely on the reference // held by the waiter. However, once this value is incremented the waiter @@ -337,6 +384,10 @@ ret = sem_post(&dsema->dsema_sem); DISPATCH_SEMAPHORE_VERIFY_RET(ret); #endif +#if USE_WIN32_SEM + // Signal the semaphore. + ret = ReleaseSemaphore(dsema->dsema_handle, 1, NULL); +#endif _dispatch_release(dsema); @@ -391,13 +442,14 @@ long _dispatch_group_wake(dispatch_semaphore_t dsema) { - struct dispatch_sema_notify_s *tmp, *head = dispatch_atomic_xchg(&dsema->dsema_notify_head, NULL); - long rval = dispatch_atomic_xchg(&dsema->dsema_group_waiters, 0); - bool do_rel = head; + struct dispatch_sema_notify_s *tmp; + struct dispatch_sema_notify_s *head = (struct dispatch_sema_notify_s *)dispatch_atomic_xchg(&dsema->dsema_notify_head, NULL); + long rval = (long)dispatch_atomic_xchg(&dsema->dsema_group_waiters, 0); + bool do_rel = (head != NULL); #if USE_MACH_SEM long kr; #endif -#if USE_POSIX_SEM +#if USE_POSIX_SEM || USE_WIN32_SEM int ret; #endif @@ -417,6 +469,11 @@ DISPATCH_SEMAPHORE_VERIFY_RET(ret); } while (--rval); #endif +#if USE_WIN32_SEM + // Signal the semaphore. + ret = ReleaseSemaphore(dsema->dsema_waiter_handle, 1, NULL); + dispatch_assume(ret); +#endif } while (head) { dispatch_async_f(head->dsn_queue, head->dsn_ctxt, head->dsn_func); @@ -440,10 +497,11 @@ #if USE_MACH_SEM mach_timespec_t _timeout; kern_return_t kr; - uint64_t nsec; #endif #if USE_POSIX_SEM struct timespec _timeout; +#endif +#if USE_POSIX_SEM || USE_WIN32_SEM int ret; #endif long orig; @@ -456,7 +514,7 @@ // Mach semaphores appear to sometimes spuriously wake up. Therefore, // we keep a parallel count of the number of times a Mach semaphore is // signaled (6880961). - dispatch_atomic_inc(&dsema->dsema_group_waiters); + (void)dispatch_atomic_inc(&dsema->dsema_group_waiters); // check the values again in case we need to wake any threads if (dsema->dsema_value == dsema->dsema_orig) { return _dispatch_group_wake(dsema); @@ -478,6 +536,7 @@ default: #if USE_MACH_SEM do { + uint64_t nsec; nsec = _dispatch_timeout(timeout); _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); @@ -500,6 +559,18 @@ break; } #endif +#if USE_WIN32_SEM + do { + uint64_t nsec; + DWORD msec; + nsec = _dispatch_timeout(timeout); + msec = (DWORD)(nsec / (uint64_t)1000000); + ret = WaitForSingleObject(dsema->dsema_waiter_handle, msec); + } while (ret != WAIT_OBJECT_0 && ret != WAIT_TIMEOUT); + if (ret == WAIT_TIMEOUT) { + break; + } +#endif /* USE_WIN32_SEM */ // Fall through and try to undo the earlier change to dsema->dsema_group_waiters case DISPATCH_TIME_NOW: while ((orig = dsema->dsema_group_waiters)) { @@ -507,7 +578,7 @@ #if USE_MACH_SEM return KERN_OPERATION_TIMED_OUT; #endif -#if USE_POSIX_SEM +#if USE_POSIX_SEM || USE_WIN32_SEM errno = ETIMEDOUT; return -1; #endif @@ -528,6 +599,12 @@ } while (ret == -1 && errno == EINTR); DISPATCH_SEMAPHORE_VERIFY_RET(ret); #endif +#if USE_WIN32_SEM + do { + ret = WaitForSingleObject(dsema->dsema_waiter_handle, INFINITE); + } while (ret != WAIT_OBJECT_0); +#endif + break; } @@ -546,7 +623,7 @@ #if USE_MACH_SEM return KERN_OPERATION_TIMED_OUT; #endif -#if USE_POSIX_SEM +#if USE_POSIX_SEM || USE_WIN32_SEM errno = ETIMEDOUT; return (-1); #endif @@ -569,7 +646,7 @@ struct dispatch_sema_notify_s *dsn, *prev; // FIXME -- this should be updated to use the continuation cache - while (!(dsn = malloc(sizeof(*dsn)))) { + while (!(dsn = (struct dispatch_sema_notify_s *)malloc(sizeof(*dsn)))) { sleep(1); } @@ -579,7 +656,7 @@ dsn->dsn_func = func; _dispatch_retain(dq); - prev = dispatch_atomic_xchg(&dsema->dsema_notify_tail, dsn); + prev = (struct dispatch_sema_notify_s *)dispatch_atomic_xchg(&dsema->dsema_notify_tail, dsn); if (fastpath(prev)) { prev->dsn_next = dsn; } else { @@ -619,7 +696,15 @@ ret = sem_destroy(&dsema->dsema_sem); DISPATCH_SEMAPHORE_VERIFY_RET(ret); #endif - +#if USE_WIN32_SEM + if (dsema->dsema_handle) { + CloseHandle(dsema->dsema_handle); + } + if (dsema->dsema_waiter_handle) { + CloseHandle(dsema->dsema_waiter_handle); + } +#endif + _dispatch_dispose(dsema); } @@ -655,7 +740,10 @@ _dispatch_retain(dg); dispatch_group_enter(dg); - dc = _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap(); + dc = _dispatch_continuation_alloc_cacheonly(); + if (dc == NULL) { + dc = _dispatch_continuation_alloc_from_heap(); + } dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT|DISPATCH_OBJ_GROUP_BIT); dc->dc_func = func; Modified: trunk/src/semaphore_internal.h =================================================================== --- trunk/src/semaphore_internal.h 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/semaphore_internal.h 2010-10-24 08:25:52 UTC (rev 192) @@ -39,13 +39,16 @@ long dsema_value; long dsema_orig; size_t dsema_sent_ksignals; -#if USE_MACH_SEM && USE_POSIX_SEM +#if (USE_MACH_SEM + USE_POSIX_SEM + USE_WIN32_SEM) > 1 #error "Too many supported semaphore types" #elif USE_MACH_SEM semaphore_t dsema_port; semaphore_t dsema_waiter_port; #elif USE_POSIX_SEM sem_t dsema_sem; +#elif USE_WIN32_SEM + HANDLE dsema_handle; + HANDLE dsema_waiter_handle; #else #error "No supported semaphore type" #endif Modified: trunk/src/shims/time.c =================================================================== --- trunk/src/shims/time.c 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/shims/time.c 2010-10-24 08:25:52 UTC (rev 192) @@ -20,17 +20,23 @@ #include "internal.h" -// for architectures that don't always return mach_absolute_time() in nanoseconds -#if !(defined(__i386__) || defined(__x86_64__) || !defined(HAVE_MACH_ABSOLUTE_TIME)) +#if !(defined(__i386__) || defined(__x86_64__)) || !HAVE_MACH_ABSOLUTE_TIME _dispatch_host_time_data_s _dispatch_host_time_data; void _dispatch_get_host_time_init(void *context __attribute__((unused))) { +#if HAVE_MACH_ABSOLUTE_TIME mach_timebase_info_data_t tbi; (void)dispatch_assume_zero(mach_timebase_info(&tbi)); _dispatch_host_time_data.frac = tbi.numer; _dispatch_host_time_data.frac /= tbi.denom; _dispatch_host_time_data.ratio_1_to_1 = (tbi.numer == tbi.denom); +#elif TARGET_OS_WIN32 + LARGE_INTEGER freq; + dispatch_assume(QueryPerformanceFrequency(&freq)); + _dispatch_host_time_data.frac = (long double)NSEC_PER_SEC / (long double)freq.QuadPart; + _dispatch_host_time_data.ratio_1_to_1 = (freq.QuadPart == 1); +#endif /* TARGET_OS_WIN32 */ } #endif Modified: trunk/src/shims/time.h =================================================================== --- trunk/src/shims/time.h 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/shims/time.h 2010-10-24 08:25:52 UTC (rev 192) @@ -31,8 +31,19 @@ #error "Please #include <dispatch/dispatch.h> instead of this file directly." #endif -#if defined(__i386__) || defined(__x86_64__) || !defined(HAVE_MACH_ABSOLUTE_TIME) -// these architectures always return mach_absolute_time() in nanoseconds +uint64_t _dispatch_get_nanoseconds(void); + +#if TARGET_OS_WIN32 +static inline unsigned int +sleep(unsigned int seconds) +{ + Sleep(seconds * 1000); // milliseconds + return 0; +} +#endif + +#if (defined(__i386__) || defined(__x86_64__)) && HAVE_MACH_ABSOLUTE_TIME +// x86 currently implements mach time in nanoseconds; this is NOT likely to change #define _dispatch_time_mach2nano(x) (x) #define _dispatch_time_nano2mach(x) (x) #else @@ -84,6 +95,12 @@ { #if HAVE_MACH_ABSOLUTE_TIME return mach_absolute_time(); +#elif TARGET_OS_WIN32 + LARGE_INTEGER now; + if (!QueryPerformanceCounter(&now)) { + return 0; + } + return now.QuadPart; #else struct timespec ts; int ret; Modified: trunk/src/time.c =================================================================== --- trunk/src/time.c 2010-10-24 07:29:08 UTC (rev 191) +++ trunk/src/time.c 2010-10-24 08:25:52 UTC (rev 192) @@ -23,12 +23,22 @@ uint64_t _dispatch_get_nanoseconds(void) { +#if !TARGET_OS_WIN32 struct timeval now; int r = gettimeofday(&now, NULL); dispatch_assert_zero(r); dispatch_assert(sizeof(NSEC_PER_SEC) == 8); dispatch_assert(sizeof(NSEC_PER_USEC) == 8); return now.tv_sec * NSEC_PER_SEC + now.tv_usec * NSEC_PER_USEC; +#else /* TARGET_OS_WIN32 */ + // FILETIME is 100-nanosecond intervals since January 1, 1601 (UTC). + FILETIME ft; + ULARGE_INTEGER li; + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + return li.QuadPart * 100ull; +#endif /* TARGET_OS_WIN32 */ } dispatch_time_t