From 88d85b6820453fc6d83ee699e33013eb6810472f Mon Sep 17 00:00:00 2001 From: Burton Li Date: Thu, 21 Mar 2019 15:10:38 -0700 Subject: [PATCH] fix NowNanos overflow (#5062) Summary: The original implementation of WinEnvIO::NowNanos() has a constant data overflow by: li.QuadPart *= std::nano::den; As a result, the api provides a incorrect result. e.g.: li.QuadPart=13477844301545 std::nano::den=1e9 The fix uses pre-computed nano_seconds_per_period_ to present the nano seconds per performance counter period, in the case if nano::den is divisible by perf_counter_frequency_. Otherwise it falls back to use high_resolution_clock. siying ajkr Pull Request resolved: https://github.com/facebook/rocksdb/pull/5062 Differential Revision: D14426842 Pulled By: anand1976 fbshipit-source-id: 127f1daf423dd4b30edd0dcf8ea0466f468bec12 --- port/win/env_win.cc | 37 +++++++++++++++++++++++++------------ port/win/env_win.h | 1 + 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/port/win/env_win.cc b/port/win/env_win.cc index d301390670..29f375dad7 100644 --- a/port/win/env_win.cc +++ b/port/win/env_win.cc @@ -73,6 +73,7 @@ WinEnvIO::WinEnvIO(Env* hosted_env) page_size_(4 * 1024), allocation_granularity_(page_size_), perf_counter_frequency_(0), + nano_seconds_per_period_(0), GetSystemTimePreciseAsFileTime_(NULL) { SYSTEM_INFO sinfo; @@ -87,6 +88,10 @@ WinEnvIO::WinEnvIO(Env* hosted_env) ret = QueryPerformanceFrequency(&qpf); assert(ret == TRUE); perf_counter_frequency_ = qpf.QuadPart; + + if (std::nano::den % perf_counter_frequency_ == 0) { + nano_seconds_per_period_ = std::nano::den / perf_counter_frequency_; + } } HMODULE module = GetModuleHandle("kernel32.dll"); @@ -955,21 +960,29 @@ uint64_t WinEnvIO::NowMicros() { return li.QuadPart; } using namespace std::chrono; - return duration_cast(system_clock::now().time_since_epoch()).count(); + return duration_cast( + high_resolution_clock::now().time_since_epoch()).count(); } uint64_t WinEnvIO::NowNanos() { - // all std::chrono clocks on windows have the same resolution that is only - // good enough for microseconds but not nanoseconds - // On Windows 8 and Windows 2012 Server - // GetSystemTimePreciseAsFileTime(¤t_time) can be used - LARGE_INTEGER li; - QueryPerformanceCounter(&li); - // Convert to nanoseconds first to avoid loss of precision - // and divide by frequency - li.QuadPart *= std::nano::den; - li.QuadPart /= perf_counter_frequency_; - return li.QuadPart; + if (nano_seconds_per_period_ != 0) { + // all std::chrono clocks on windows have the same resolution that is only + // good enough for microseconds but not nanoseconds + // On Windows 8 and Windows 2012 Server + // GetSystemTimePreciseAsFileTime(¤t_time) can be used + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + // Convert performance counter to nanoseconds by precomputed ratio. + // Directly multiply nano::den with li.QuadPart causes overflow. + // Only do this when nano::den is divisible by perf_counter_frequency_, + // which most likely is the case in reality. If it's not, fall back to + // high_resolution_clock, which may be less precise under old compilers. + li.QuadPart *= nano_seconds_per_period_; + return li.QuadPart; + } + using namespace std::chrono; + return duration_cast( + high_resolution_clock::now().time_since_epoch()).count(); } Status WinEnvIO::GetHostName(char* name, uint64_t len) { diff --git a/port/win/env_win.h b/port/win/env_win.h index d61ac3acd6..cb042b1b6f 100644 --- a/port/win/env_win.h +++ b/port/win/env_win.h @@ -198,6 +198,7 @@ private: size_t page_size_; size_t allocation_granularity_; uint64_t perf_counter_frequency_; + uint64_t nano_seconds_per_period_; FnGetSystemTimePreciseAsFileTime GetSystemTimePreciseAsFileTime_; };