From 22494057dfeadfffc3e2a504ff73233fbae80299 Mon Sep 17 00:00:00 2001 From: Marcin Mielniczuk Date: Wed, 6 Nov 2019 17:02:58 +0100 Subject: [PATCH] Try to somehow implement clock_res_get on Windows. (#124) * Implement clock_time_get on Windows. Also update misc_testsuite to include latest clock_time_get test changes. * Try to somehow implement clock_res_get on Windows. * Fix 55ms * Cache the perf counter resolution * Fix integration tests --- Cargo.toml | 3 ++ src/sys/windows/hostcalls_impl/misc.rs | 58 +++++++++++++++++++++++++- winx/src/lib.rs | 1 + winx/src/time.rs | 10 +++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 winx/src/time.rs diff --git a/Cargo.toml b/Cargo.toml index 9c3f181cda..85c3eda47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,9 @@ cranelift-codegen = "0.46.1" target-lexicon = "0.8.1" pretty_env_logger = "0.3.0" tempfile = "3.1.0" +# this is just a temp fix to make the tests build and run; I hope this to be +# completely removed when we merge `wasi-common` into `wasmtime` +faerie = "=0.11.0" [patch."https://github.com/CraneStation/wasi-common"] wasi-common = { path = "." } diff --git a/src/sys/windows/hostcalls_impl/misc.rs b/src/sys/windows/hostcalls_impl/misc.rs index a4c7a290f6..b77503f1f3 100644 --- a/src/sys/windows/hostcalls_impl/misc.rs +++ b/src/sys/windows/hostcalls_impl/misc.rs @@ -13,10 +13,57 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; lazy_static! { static ref START_MONOTONIC: Instant = Instant::now(); + static ref PERF_COUNTER_RES: u64 = get_perf_counter_resolution_ns(); } +// Timer resolution on Windows is really hard. We may consider exposing the resolution of the respective +// timers as an associated function in the future. pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result { - unimplemented!("clock_res_get") + Ok(match clock_id { + // This is the best that we can do with std::time::SystemTime. + // Rust uses GetSystemTimeAsFileTime, which is said to have the resolution of + // 10ms or 55ms, [1] but MSDN doesn't confirm this in any way. + // Even the MSDN article on high resolution timestamps doesn't even mention the precision + // for this method. [3] + // + // The timer resolution can be queried using one of the functions: [2, 5] + // * NtQueryTimerResolution, which is undocumented and thus not exposed by the winapi crate + // * timeGetDevCaps, which returns the upper and lower bound for the precision, in ms. + // While the upper bound seems like something we could use, it's typically too high to be meaningful. + // For instance, the intervals return by the syscall are: + // * [1, 65536] on Wine + // * [1, 1000000] on Windows 10, which is up to (sic) 1000 seconds. + // + // It's possible to manually set the timer resolution, but this sounds like something which should + // only be done temporarily. [5] + // + // Alternatively, we could possibly use GetSystemTimePreciseAsFileTime in clock_time_get, but + // this syscall is only available starting from Windows 8. + // (we could possibly emulate it on earlier versions of Windows, see [4]) + // The MSDN are not clear on the resolution of GetSystemTimePreciseAsFileTime either, but a + // Microsoft devblog entry [1] suggests that it kind of combines GetSystemTimeAsFileTime with + // QueryPeformanceCounter, which probably means that those two should have the same resolution. + // + // See also this discussion about the use of GetSystemTimePreciseAsFileTime in Python stdlib, + // which in particular contains some resolution benchmarks. + // + // [1] https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057 + // [2] http://www.windowstimestamp.com/description + // [3] https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps?redirectedfrom=MSDN + // [4] https://www.codeproject.com/Tips/1011902/High-Resolution-Time-For-Windows + // [5] https://stackoverflow.com/questions/7685762/windows-7-timing-functions-how-to-use-getsystemtimeadjustment-correctly + // [6] https://bugs.python.org/issue19007 + wasi::__WASI_CLOCK_REALTIME => 55_000_000, + // std::time::Instant uses QueryPerformanceCounter & QueryPerformanceFrequency internally + wasi::__WASI_CLOCK_MONOTONIC => *PERF_COUNTER_RES, + // The best we can do is to hardcode the value from the docs. + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocesstimes + wasi::__WASI_CLOCK_PROCESS_CPUTIME_ID => 100, + // The best we can do is to hardcode the value from the docs. + // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getthreadtimes + wasi::__WASI_CLOCK_THREAD_CPUTIME_ID => 100, + _ => return Err(Error::EINVAL), + }) } pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result { @@ -61,3 +108,12 @@ fn get_proc_cputime() -> Result { fn get_thread_cputime() -> Result { Ok(ThreadTime::try_now()?.as_duration()) } + +fn get_perf_counter_resolution_ns() -> u64 { + use winx::time::perf_counter_frequency; + const NANOS_PER_SEC: u64 = 1_000_000_000; + // This should always succeed starting from Windows XP, so it's fine to panic in case of an error. + let freq = perf_counter_frequency().expect("QueryPerformanceFrequency returned an error"); + let epsilon = NANOS_PER_SEC / freq; + epsilon +} diff --git a/winx/src/lib.rs b/winx/src/lib.rs index f017a7fe2b..44c65b44ba 100644 --- a/winx/src/lib.rs +++ b/winx/src/lib.rs @@ -25,6 +25,7 @@ extern crate bitflags; pub mod file; +pub mod time; pub mod winerror; use winerror::WinError; diff --git a/winx/src/time.rs b/winx/src/time.rs new file mode 100644 index 0000000000..28803e1fb6 --- /dev/null +++ b/winx/src/time.rs @@ -0,0 +1,10 @@ +use cvt::cvt; +use winapi::um::{profileapi::QueryPerformanceFrequency, winnt::LARGE_INTEGER}; + +pub fn perf_counter_frequency() -> std::io::Result { + unsafe { + let mut frequency: LARGE_INTEGER = std::mem::zeroed(); + cvt(QueryPerformanceFrequency(&mut frequency))?; + Ok(*frequency.QuadPart() as u64) + } +}