From 8dcbc86b973ec5a0fcee03e8cbc86af1b2212178 Mon Sep 17 00:00:00 2001 From: Con Kolivas Date: Wed, 23 Oct 2013 09:55:10 +1100 Subject: [PATCH] Use windows' own higher resolution time and handlers allowing us to have higher precision absolute timeouts. --- util.c | 133 +++++++++++++++++++++++++++++++++++++++++---------------- util.h | 4 ++ 2 files changed, 100 insertions(+), 37 deletions(-) diff --git a/util.c b/util.c index 5b907505..87d7bac6 100644 --- a/util.c +++ b/util.c @@ -899,22 +899,11 @@ void timeraddspec(struct timespec *a, const struct timespec *b) } } -static int timespec_to_ms(struct timespec *ts) +static int __maybe_unused timespec_to_ms(struct timespec *ts) { return ts->tv_sec * 1000 + ts->tv_nsec / 1000000; } -/* Subtracts b from a and stores it in res. */ -void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) -{ - res->tv_sec = a->tv_sec - b->tv_sec; - res->tv_nsec = a->tv_nsec - b->tv_nsec; - if (res->tv_nsec < 0) { - res->tv_nsec += 1000000000; - res->tv_sec--; - } -} - /* Subtract b from a */ static void __maybe_unused timersubspec(struct timespec *a, const struct timespec *b) { @@ -926,23 +915,6 @@ static void __maybe_unused timersubspec(struct timespec *a, const struct timespe } } -static void __maybe_unused cgsleep_spec(struct timespec *ts_diff, const struct timespec *ts_start) -{ - struct timespec now; - - timeraddspec(ts_diff, ts_start); - cgtimer_time(&now); - timersubspec(ts_diff, &now); - if (unlikely(ts_diff->tv_sec < 0)) - return; - nanosleep(ts_diff, NULL); -} - -int cgtimer_to_ms(cgtimer_t *cgt) -{ - return timespec_to_ms(cgt); -} - /* These are cgminer specific sleep functions that use an absolute nanosecond * resolution timer to avoid poor usleep accuracy and overruns. */ #ifdef WIN32 @@ -976,19 +948,27 @@ void cgtime(struct timeval *tv) tv->tv_usec = lidiv.rem / 10; } -void cgtimer_time(cgtimer_t *ts_start) -{ - lldiv_t lidiv;; - - decius_time(&lidiv); - ts_start->tv_sec = lidiv.quot; - ts_start->tv_nsec = lidiv.rem * 100; -} #else /* WIN32 */ void cgtime(struct timeval *tv) { gettimeofday(tv, NULL); } + +int cgtimer_to_ms(cgtimer_t *cgt) +{ + return timespec_to_ms(cgt); +} + +/* Subtracts b from a and stores it in res. */ +void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + if (res->tv_nsec < 0) { + res->tv_nsec += 1000000000; + res->tv_sec--; + } +} #endif /* WIN32 */ #ifdef CLOCK_MONOTONIC /* Essentially just linux */ @@ -1051,6 +1031,84 @@ void cgtimer_time(cgtimer_t *ts_start) ts_start->tv_nsec = tv->tv_usec * 1000; } #endif /* __MACH__ */ + +#ifdef WIN32 +/* For windows we use the SystemTime stored as a LARGE_INTEGER as the cgtimer_t + * typedef, allowing us to have sub-microsecond resolution for times, do simple + * arithmetic for timer calculations, and use windows' own hTimers to get + * accurate absolute timeouts. */ +int cgtimer_to_ms(cgtimer_t *cgt) +{ + return (int)(cgt->QuadPart / 10000LL); +} + +/* Subtracts b from a and stores it in res. */ +void cgtimer_sub(cgtimer_t *a, cgtimer_t *b, cgtimer_t *res) +{ + res->QuadPart = a->QuadPart - b->QuadPart; +} + +/* Note that cgtimer time is NOT offset by the unix epoch since we use absolute + * timeouts with hTimers. */ +void cgtimer_time(cgtimer_t *ts_start) +{ + FILETIME ft; + + GetSystemTimeAsFileTime(&ft); + ts_start->LowPart = ft.dwLowDateTime; + ts_start->HighPart = ft.dwHighDateTime; +} + +static void liSleep(LARGE_INTEGER *li, int timeout) +{ + HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL); + DWORD ret; + + if (unlikely(!hTimer)) + quit(1, "Failed to create hTimer in liSleep"); + ret = SetWaitableTimer(hTimer, li, 0, NULL, NULL, 0); + if (unlikely(!ret)) + quit(1, "Failed to SetWaitableTimer in liSleep"); + /* We still use a timeout as a sanity check in case the system time + * is changed while we're running */ + ret = WaitForSingleObject(hTimer, timeout); + if (unlikely(ret != WAIT_OBJECT_0 && ret != WAIT_TIMEOUT)) + quit(1, "Failed to WaitForSingleObject in liSleep"); + CloseHandle(hTimer); +} + +void cgsleep_ms_r(cgtimer_t *ts_start, int ms) +{ + LARGE_INTEGER li; + + li.QuadPart = ts_start->QuadPart + (int64_t)ms * 10000LL; + liSleep(&li, ms); +} + +void cgsleep_us_r(cgtimer_t *ts_start, int64_t us) +{ + LARGE_INTEGER li; + int ms; + + li.QuadPart = ts_start->QuadPart + us * 10LL; + ms = us / 1000; + if (!ms) + ms = 1; + liSleep(&li, ms); +} +#else /* WIN32 */ +static void cgsleep_spec(struct timespec *ts_diff, const struct timespec *ts_start) +{ + struct timespec now; + + timeraddspec(ts_diff, ts_start); + cgtimer_time(&now); + timersubspec(ts_diff, &now); + if (unlikely(ts_diff->tv_sec < 0)) + return; + nanosleep(ts_diff, NULL); +} + void cgsleep_ms_r(cgtimer_t *ts_start, int ms) { struct timespec ts_diff; @@ -1066,6 +1124,7 @@ void cgsleep_us_r(cgtimer_t *ts_start, int64_t us) us_to_timespec(&ts_diff, us); cgsleep_spec(&ts_diff, ts_start); } +#endif /* WIN32 */ #endif /* CLOCK_MONOTONIC */ void cgsleep_ms(int ms) diff --git a/util.h b/util.h index 61fb3516..a32988bc 100644 --- a/util.h +++ b/util.h @@ -85,7 +85,11 @@ typedef struct cgsem cgsem_t; #else typedef sem_t cgsem_t; #endif +#ifdef WIN32 +typedef LARGE_INTEGER cgtimer_t; +#else typedef struct timespec cgtimer_t; +#endif struct thr_info; struct pool;