Browse Source

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
pull/502/head
Marcin Mielniczuk 5 years ago
committed by Jakub Konka
parent
commit
22494057df
  1. 3
      Cargo.toml
  2. 58
      src/sys/windows/hostcalls_impl/misc.rs
  3. 1
      winx/src/lib.rs
  4. 10
      winx/src/time.rs

3
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 = "." }

58
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<wasi::__wasi_timestamp_t> {
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<wasi::__wasi_timestamp_t> {
@ -61,3 +108,12 @@ fn get_proc_cputime() -> Result<Duration> {
fn get_thread_cputime() -> Result<Duration> {
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
}

1
winx/src/lib.rs

@ -25,6 +25,7 @@
extern crate bitflags;
pub mod file;
pub mod time;
pub mod winerror;
use winerror::WinError;

10
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<u64> {
unsafe {
let mut frequency: LARGE_INTEGER = std::mem::zeroed();
cvt(QueryPerformanceFrequency(&mut frequency))?;
Ok(*frequency.QuadPart() as u64)
}
}
Loading…
Cancel
Save