diff --git a/test/lib/timer.c b/test/lib/timer.c index ade8c592..fd158f32 100644 --- a/test/lib/timer.c +++ b/test/lib/timer.c @@ -259,14 +259,14 @@ static void int128MulDiv64(timerprivInt128 *x, timerprivInt128 *y, timerprivInt1 numer.low = x64low * y64low; // b * d + add.neg = 0; add.high = x64high * y64low; // a * d * 2^32 + - add.low = add.high & 0xFFFFFFFF00000000; + add.low = (add.high & 0xFFFFFFFF) << 32; add.high >>= 32; int128UAdd(&numer, &add); add.high = x64low * y64high; // b * c * 2^32 - add.low = add.high & 0xFFFFFFFF00000000; + add.low = (add.high & 0xFFFFFFFF) << 32; add.high >>= 32; int128UAdd(&numer, &add); - // I did type this all by hand, btw; the idea does come from Apple's implementation, though they explain it a bit more obtusely, and the odd behavior with anding high and shifting it right is to avoid looking like I directly copied their code which does the opposite + // I did type this all by hand, btw; the idea does come from Apple's implementation, though they explain it a bit more obtusely, and the odd behavior with anding high into low is to avoid looking like I directly copied their code which does the opposite // and now long-divide // Apple's implementation uses Newton–Raphson division using doubles to store 1/z but I'd rather go with "slow but guaranteed to be accurate" @@ -286,7 +286,7 @@ static void int128MulDiv64(timerprivInt128 *x, timerprivInt128 *y, timerprivInt1 } } -void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot) +void timerprivMulDivInt64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot) { timerprivInt128 a, b, c; @@ -296,7 +296,7 @@ void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot) int128MulDiv64(&a, &b, &c, quot); } -void timerprivMulDivU64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot) +void timerprivMulDivUint64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot) { timerprivInt128 a, b, c; diff --git a/test/lib/timer.h b/test/lib/timer.h index 4d19c6d4..760c6ff3 100644 --- a/test/lib/timer.h +++ b/test/lib/timer.h @@ -5,6 +5,8 @@ typedef int64_t timerDuration; typedef int64_t timerTime; +#define timerTimeMax ((timerTime) INT64_MAX) + #define timerNanosecond ((timerDuration) 1) #define timerMicrosecond ((timerDuration) 1000) #define timerMillisecond ((timerDuration) 1000000) diff --git a/test/lib/timer_darwinunix.c b/test/lib/timer_darwinunix.c index a1f9c5f2..dda27d5a 100644 --- a/test/lib/timer_darwinunix.c +++ b/test/lib/timer_darwinunix.c @@ -15,24 +15,50 @@ #include #endif #include "timer.h" +#include "timerpriv.h" + +static void mustpthread_once(pthread_once_t *once, void (*init)(void)) +{ + int err; + + err = pthread_once(once, init); + if (err != 0) { + fprintf(stderr, "*** internal error in timerMonotonicNow(): pthread_once() failed: %s (%d)\n", strerror(err), err); + abort(); + } +} #ifdef __APPLE__ -timerTime timerMonotonicNow(void) +static uint64_t base; +static mach_timebase_info_data_t mt; +static pthread_once_t baseOnce = PTHREAD_ONCE_INIT; + +static void baseInit(void) { - return (timerTime) mach_absolute_time(); + kern_return_t err; + + base = mach_absolute_time(); + err = mach_timebase_info(&mt); + if (err != KERN_SUCCESS) { + fprintf(stderr, "*** internal error in timerMonotonicNow(): mach_timebase_info() failed: kern_return_t %d\n", err); + abort(); + } } -timerDuration timerTimeSub(timerTime end, timerTime start) +timerTime timerMonotonicNow(void) { - mach_timebase_info_data_t mt; - timerTime c; - timerDuration ret; + uint64_t t; + timerprivInt128 quot; - mach_timebase_info(&mt); - c = end - start; - ret = ((timerDuration) c) * mt.numer / mt.denom; - return ret; + mustpthread_once(&baseOnce, baseInit); + t = mach_absolute_time() - base; + timerprivMulDivUint64(t, mt.numer, mt.denom, "); + // on overflow, return the maximum possible timerTime; this is inspired by what Go does + if (quot.high == 0) + if (quot.low <= ((uint64_t) timerTimeMax)) + return (timerTime) (quot.low); + return timerTimeMax; } #else @@ -61,13 +87,8 @@ timerTime timerMonotonicNow(void) { struct timespec ts; timerTime ret; - int err; - err = pthread_once(&baseOnce, baseInit); - if (err != 0) { - fprintf(stderr, "*** internal error in timerMonotonicNow(): pthread_once() failed: %s (%d)\n", strerror(err), err); - abort(); - } + mustpthread_once(&baseOnce, baseInit); mustclock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_sec -= base.tv_sec; ts.tv_nsec -= base.tv_nsec; @@ -80,13 +101,13 @@ timerTime timerMonotonicNow(void) return ret; } +#endif + timerDuration timerTimeSub(timerTime end, timerTime start) { return end - start; } -#endif - struct timeoutParams { jmp_buf retpos; struct itimerval prevDuration; diff --git a/test/lib/timerpriv.h b/test/lib/timerpriv.h index fccd787b..37fc7173 100644 --- a/test/lib/timerpriv.h +++ b/test/lib/timerpriv.h @@ -8,5 +8,5 @@ struct timerprivInt128 { uint64_t low; }; -extern void timerprivMulDiv64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot); -extern void timerprivMulDivU64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot); +extern void timerprivMulDivInt64(int64_t x, int64_t y, int64_t z, timerprivInt128 *quot); +extern void timerprivMulDivUint64(uint64_t x, uint64_t y, uint64_t z, timerprivInt128 *quot);