Skip to content
This repository has been archived by the owner on Nov 9, 2019. It is now read-only.

Commit

Permalink
Try to somehow implement clock_res_get on Windows. (#124)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
marmistrz authored and kubkon committed Nov 6, 2019
1 parent 3757b8b commit 2249405
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "." }
Expand Down
58 changes: 57 additions & 1 deletion src/sys/windows/hostcalls_impl/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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 change: 1 addition & 0 deletions winx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
extern crate bitflags;

pub mod file;
pub mod time;
pub mod winerror;

use winerror::WinError;
Expand Down
10 changes: 10 additions & 0 deletions winx/src/time.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}

0 comments on commit 2249405

Please sign in to comment.