From 2523170806ec25f622229a066f9033cc0e4769ef Mon Sep 17 00:00:00 2001 From: Leo Yuriev Date: Fri, 21 Apr 2017 18:33:35 +0300 Subject: [PATCH] test: add rnd and delay tools. --- test/chrono.cc | 33 +++++++- test/chrono.h | 33 ++++++-- test/osal-unix.cc | 47 +++++++++++ test/osal-windows.cc | 32 ++++++++ test/osal.h | 6 +- test/utils.cc | 192 +++++++++++++++++++++++++++++++++++++++++++ test/utils.h | 54 ++++++++++++ 7 files changed, 389 insertions(+), 8 deletions(-) diff --git a/test/chrono.cc b/test/chrono.cc index 8c9dcebe..3481ad74 100644 --- a/test/chrono.cc +++ b/test/chrono.cc @@ -71,7 +71,7 @@ time from_ms(uint64_t ms) { return result; } -time now() { +time now_realtime() { #if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) FILETIME filetime; GetSystemTimeAsFileTime(&filetime); @@ -87,4 +87,35 @@ time now() { #endif } +time now_motonic() { +#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) + static uint32_t reciprocal; + static LARGE_INTEGER Frequency; + if (reciprocal == 0) { + if (!QueryPerformanceFrequency(&Frequency)) + failure_perror("QueryPerformanceFrequency()", GetLastError()); + reciprocal = (UINT64_C(1) << 32) / Frequency.QuadPart; + assert(reciprocal); + } + + LARGE_INTEGER Counter; + if (!QueryPerformanceCounter(&Counter)) + failure_perror("QueryPerformanceCounter()", GetLastError()); + + time result; + result.integer = Counter.QuadPart / Frequency.QuadPart; + uint64_t mod = Counter.QuadPart % Frequency.QuadPart; + assert(mod < UINT32_MAX); + result.fractional = UInt32x32To64((uint32_t)mod, reciprocal); + assert(result.fractional == (mod << 32) / Frequency.QuadPart); + return result; +#else + struct timespec ts; + if (unlikely(clock_gettime(CLOCK_MONOTONIC, &ts))) + failure_perror("clock_gettime(CLOCK_MONOTONIC)", errno); + + return from_timespec(ts); +#endif +} + } /* namespace chrono */ diff --git a/test/chrono.h b/test/chrono.h index bb90e8e5..b417f1e1 100644 --- a/test/chrono.h +++ b/test/chrono.h @@ -25,12 +25,21 @@ typedef union time { struct __packed { #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ uint32_t fractional; - uint32_t utc; + union { + uint32_t utc; + uint32_t integer; + }; #else - uint32_t utc; + union { + uint32_t utc; + uint32_t integer; + }; uint32_t fractional; #endif }; + + void reset() { fixedpoint = 0; } + uint32_t seconds() const { return utc; } } time; uint32_t ns2fractional(uint32_t); @@ -44,10 +53,21 @@ time from_ns(uint64_t us); time from_us(uint64_t ns); time from_ms(uint64_t ms); -inline time from_utc(time_t utc) { - assert(utc < UINT32_MAX); +inline time from_seconds(uint64_t seconds) { + assert(seconds < UINT32_MAX); time result; - result.fixedpoint = ((uint64_t)utc) << 32; + result.fixedpoint = seconds << 32; + return result; +} + +inline time from_utc(time_t utc) { + assert(utc >= 0); + return from_seconds(utc); +} + +inline time infinite() { + time result; + result.fixedpoint = UINT64_MAX; return result; } @@ -70,6 +90,7 @@ inline time from_timeval(const struct timeval &tv) { } #endif /* HAVE_TIMEVAL_TV_USEC */ -time now(); +time now_realtime(); +time now_motonic(); } /* namespace chrono */ diff --git a/test/osal-unix.cc b/test/osal-unix.cc index 5131181c..5e475705 100644 --- a/test/osal-unix.cc +++ b/test/osal-unix.cc @@ -157,6 +157,8 @@ static void handler_SIGCHLD(int unused) { (void)unused; } mdbx_pid_t osal_getpid(void) { return getpid(); } +int osal_delay(unsigned seconds) { return sleep(seconds) ? errno : 0; } + int osal_actor_start(const actor_config &config, mdbx_pid_t &pid) { if (childs.empty()) signal(SIGCHLD, handler_SIGCHLD); @@ -228,3 +230,48 @@ retry: return errno; } } + +void osal_yield(void) { + if (sched_yield()) + failure_perror("sched_yield()", errno); +} + +void osal_udelay(unsigned us) { + chrono::time until, now = chrono::now_motonic(); + until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint; + struct timespec ts; + + static unsigned threshold_us; + if (threshold_us == 0) { + if (clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts)) { + int rc = errno; + failure_perror("clock_getres(CLOCK_PROCESS_CPUTIME_ID)", rc); + } + chrono::time threshold = chrono::from_timespec(ts); + assert(threshold.seconds() == 0); + + threshold_us = chrono::fractional2us(threshold.fractional); + if (threshold_us < 1000) + threshold_us = 1000; + } + + ts.tv_sec = ts.tv_nsec = 0; + if (us > threshold_us) { + ts.tv_sec = us / 1000000u; + ts.tv_nsec = (us % 1000000u) * 1000u; + } + + do { + if (us > threshold_us) { + if (nanosleep(&ts, &ts)) { + int rc = errno; + /* if (rc == EINTR) { ... } ? */ + failure_perror("usleep()", rc); + } + us = ts.tv_sec * 1000000u + ts.tv_nsec / 1000u; + } + cpu_relax(); + + now = chrono::now_motonic(); + } while (until.fixedpoint > now.fixedpoint); +} diff --git a/test/osal-windows.cc b/test/osal-windows.cc index d4083c81..fc3445cc 100644 --- a/test/osal-windows.cc +++ b/test/osal-windows.cc @@ -97,6 +97,11 @@ int osal_waitfor(unsigned id) { mdbx_pid_t osal_getpid(void) { return GetCurrentProcessId(); } +int osal_delay(unsigned seconds) { + Sleep(seconds * 1000u); + return 0; +} + //----------------------------------------------------------------------------- const std::string @@ -260,3 +265,30 @@ int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout) { return waitstatus2errcode(rc); } + +void osal_yield(void) { SwitchToThread(); } + +void osal_udelay(unsigned us) { + chrono::time until, now = chrono::now_motonic(); + until.fixedpoint = now.fixedpoint + chrono::from_us(us).fixedpoint; + + static unsigned threshold_us; + if (threshold_us == 0) { + ULONGLONG InterruptTimePrecise_100ns; + QueryInterruptTimePrecise(&InterruptTimePrecise_100ns); + threshold_us = InterruptTimePrecise_100ns / 5; + assert(threshold_us > 0); + } + + do { + if (us > threshold_us && us > 1000) { + DWORD rc = SleepEx(us / 1000, TRUE); + if (rc) + failure_perror("SleepEx()", waitstatus2errcode(rc)); + us = 0; + } + + YieldProcessor(); + now = chrono::now_motonic(); + } while (now.fixedpoint < until.fixedpoint); +} diff --git a/test/osal.h b/test/osal.h index 1e5de123..7eac2ad8 100644 --- a/test/osal.h +++ b/test/osal.h @@ -20,9 +20,13 @@ void osal_setup(const std::vector &actors); void osal_broadcast(unsigned id); int osal_waitfor(unsigned id); -mdbx_pid_t osal_getpid(void); int osal_actor_start(const actor_config &config, mdbx_pid_t &pid); actor_status osal_actor_info(const mdbx_pid_t pid); void osal_killall_actors(void); int osal_actor_poll(mdbx_pid_t &pid, unsigned timeout); void osal_wait4barrier(void); + +mdbx_pid_t osal_getpid(void); +int osal_delay(unsigned seconds); +void osal_udelay(unsigned us); +void osal_yield(void); diff --git a/test/utils.cc b/test/utils.cc index c3be0ec0..ae6797f8 100644 --- a/test/utils.cc +++ b/test/utils.cc @@ -13,6 +13,8 @@ */ #include "test.h" +#include +#include std::string format(const char *fmt, ...) { va_list ap, ones; @@ -88,3 +90,193 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr, } //----------------------------------------------------------------------------- + +#ifdef __mips__ +static uint64_t *mips_tsc_addr; + +__cold static void mips_rdtsc_init() { + int mem_fd = open("/dev/mem", O_RDONLY | O_SYNC, 0); + HIPPEUS_ENSURE(mem_fd >= 0); + + mips_tsc_addr = mmap(nullptr, pagesize, PROT_READ, MAP_SHARED, mem_fd, + 0x10030000 /* MIPS_ZBUS_TIMER */); + close(mem_fd); +} +#endif /* __mips__ */ + +uint64_t entropy_ticks(void) { +#if defined(__GNUC__) || defined(__clang__) +#if defined(__ia64__) + uint64_t ticks; + __asm("mov %0=ar.itc" : "=r"(ticks)); + return ticks; +#elif defined(__hppa__) + uint64_t ticks; + __asm("mfctl 16, %0" : "=r"(ticks)); + return ticks; +#elif defined(__s390__) + uint64_t ticks; + __asm("stck 0(%0)" : : "a"(&(ticks)) : "memory", "cc"); + return ticks; +#elif defined(__alpha__) + uint64_t ticks; + __asm("rpcc %0" : "=r"(ticks)); + return ticks; +#elif defined(__sparc_v9__) + uint64_t ticks; + __asm("rd %%tick, %0" : "=r"(ticks)); + return ticks; +#elif defined(__powerpc64__) || defined(__ppc64__) + uint64_t ticks; + __asm("mfspr %0, 268" : "=r"(ticks)); + return ticks; +#elif defined(__ppc__) || defined(__powerpc__) + unsigned tbl, tbu; + + /* LY: Here not a problem if a high-part (tbu) + * would been updated during reading. */ + __asm("mftb %0" : "=r"(tbl)); + __asm("mftbu %0" : "=r"(tbu)); + + return (((uin64_t)tbu0) << 32) | tbl; +#elif defined(__mips__) + if (mips_tsc_addr != MAP_FAILED) { + if (unlikely(!mips_tsc_addr)) { + static pthread_once_t is_initialized = PTHREAD_ONCE_INIT; + int rc = pthread_once(&is_initialized, mips_rdtsc_init); + if (unlikely(rc)) + failure_perror("pthread_once()", rc); + } + if (mips_tsc_addr != MAP_FAILED) + return *mips_tsc_addr; + } +#elif defined(__x86_64__) || defined(__i386__) + unsigned lo, hi; + + /* LY: Using the "a" and "d" constraints is important for correct code. */ + __asm("rdtsc" : "=a"(lo), "=d"(hi)); + + return (((uint64_t)hi) << 32) + lo; +#endif /* arch selector */ + +#elif defined(_M_IX86) || defined(_M_X64) + return __rdtsc(); +#endif /* __GNUC__ || __clang__ */ + +#if defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) + LARGE_INTEGER PerformanceCount; + if (QueryPerformanceCounter(&PerformanceCount)) + return PerformanceCount.QuadPart; + return GetTickCount64(); +#else + struct timespec ts; +#if defined(CLOCK_MONOTONIC_COARSE) + clockid_t clock = CLOCK_MONOTONIC_COARSE; +#elif defined(CLOCK_MONOTONIC_RAW) + clockid_t clock = CLOCK_MONOTONIC_RAW; +#else + clockid_t clock = CLOCK_MONOTONIC; +#endif + int rc = clock_gettime(clock, &ts); + if (unlikely(rc)) + failure_perror("clock_gettime()", rc); + + return (((uint64_t)ts.tv_sec) << 32) + ts.tv_nsec; +#endif +} + +//----------------------------------------------------------------------------- + +static __inline uint64_t bleach64(uint64_t dirty) { + dirty = mul_64x64_high(bswap64(dirty), UINT64_C(17048867929148541611)); + return dirty; +} + +static __inline uint32_t bleach32(uint32_t dirty) { + return (uint32_t)( + (bswap32(dirty) * UINT64_C(/*3080105489, 4267077937 */ 2175734609)) >> + 32); +} + +uint64_t prng64_careless(uint64_t &state) { + state = state * UINT64_C(6364136223846793005) + 1; + return state; +} + +uint64_t prng64_white(uint64_t &state) { + state = state * UINT64_C(6364136223846793005) + UINT64_C(1442695040888963407); + return bleach64(state); +} + +uint32_t prng32(uint64_t &state) { + return (uint32_t)(prng64_careless(state) >> 32); +} + +uint64_t entropy_white() { return bleach64(entropy_ticks()); } + +double double_from_lower(uint64_t salt) { +#ifdef IEEE754_DOUBLE_BIAS + ieee754_double r; + r.ieee.negative = 0; + r.ieee.exponent = IEEE754_DOUBLE_BIAS; + r.ieee.mantissa0 = (unsigned)(salt >> 32); + r.ieee.mantissa1 = (unsigned)salt; + return r.d; +#else + const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1; + const double scale = 1.0 / (double)top; + return (salt & top) * scale; +#endif +} + +double double_from_upper(uint64_t salt) { +#ifdef IEEE754_DOUBLE_BIAS + ieee754_double r; + r.ieee.negative = 0; + r.ieee.exponent = IEEE754_DOUBLE_BIAS; + salt >>= 64 - DBL_MANT_DIG; + r.ieee.mantissa0 = (unsigned)(salt >> 32); + r.ieee.mantissa1 = (unsigned)salt; + return r.d; +#else + const uint64_t top = (UINT64_C(1) << DBL_MANT_DIG) - 1; + const double scale = 1.0 / (double)top; + return (salt >> (64 - DBL_MANT_DIG)) * scale; +#endif +} + +bool flipcoin() { return bleach32((uint32_t)entropy_ticks()) & 1; } + +bool jitter(unsigned probability_percent) { + const uint32_t top = UINT32_MAX - UINT32_MAX % 100; + uint32_t dice, edge = (top) / 100 * probability_percent; + do + dice = bleach32((uint32_t)entropy_ticks()); + while (dice >= top); + return dice < edge; +} + +void jitter_delay(bool extra) { + unsigned dice = entropy_white() & 3; + if (dice == 0) { + log_trace("== jitter.no-delay"); + } else { + log_trace(">> jitter.delay: dice %u", dice); + do { + cpu_relax(); + memory_barrier(); + cpu_relax(); + if (dice > 1) { + osal_yield(); + cpu_relax(); + if (dice > 2) { + unsigned us = entropy_white() & + (extra ? 0xfffff /* 1.05 s */ : 0x3fff /* 16 ms */); + log_trace("== jitter.delay: %0.6f", us / 1000000.0); + osal_udelay(us); + } + } + } while (flipcoin()); + log_trace("<< jitter.delay: dice %u", dice); + } +} diff --git a/test/utils.h b/test/utils.h index 55d8f6fd..b4c88834 100644 --- a/test/utils.h +++ b/test/utils.h @@ -271,6 +271,50 @@ static __inline size_t roundup2(size_t value, size_t granularity) { //----------------------------------------------------------------------------- +static __inline void memory_barrier(void) { +#if __has_extension(c_atomic) || __has_extension(cxx_atomic) + __c11_atomic_thread_fence(__ATOMIC_SEQ_CST); +#elif defined(__ATOMIC_SEQ_CST) + __atomic_thread_fence(__ATOMIC_SEQ_CST); +#elif defined(__clang__) || defined(__GNUC__) + __sync_synchronize(); +#elif defined(_MSC_VER) + MemoryBarrier(); +#elif defined(__INTEL_COMPILER) /* LY: Intel Compiler may mimic GCC and MSC */ +#if defined(__ia64__) || defined(__ia64) || defined(_M_IA64) + __mf(); +#elif defined(__i386__) || defined(__x86_64__) + _mm_mfence(); +#else +#error "Unknown target for Intel Compiler, please report to us." +#endif +#elif defined(__SUNPRO_C) || defined(__sun) || defined(sun) + __machine_rw_barrier(); +#elif (defined(_HPUX_SOURCE) || defined(__hpux) || defined(__HP_aCC)) && \ + (defined(HP_IA64) || defined(__ia64)) + _Asm_mf(); +#elif defined(_AIX) || defined(__ppc__) || defined(__powerpc__) || \ + defined(__ppc64__) || defined(__powerpc64__) + __lwsync(); +#else +#error "Could not guess the kind of compiler, please report to us." +#endif +} + +static __inline void cpu_relax() { +#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || \ + defined(_M_X64) + _mm_pause(); +#elif defined(_WIN32) || defined(_WIN64) || defined(_WINDOWS) || \ + defined(YieldProcessor) + YieldProcessor(); +#else +/* nope */ +#endif +} + +//----------------------------------------------------------------------------- + struct simple_checksum { uint64_t value; @@ -310,3 +354,13 @@ bool hex2data(const char *hex_begin, const char *hex_end, void *ptr, size_t bytes, simple_checksum &checksum); std::string format(const char *fmt, ...); + +uint64_t entropy_ticks(void); +uint64_t entropy_white(void); +uint64_t prng64_careless(uint64_t &state); +uint64_t prng64_white(uint64_t &state); +uint32_t prng32(uint64_t &state); + +bool flipcoin(); +bool jitter(unsigned probability_percent); +void jitter_delay(bool extra = false);