Skip to content

Commit ac7bb4b

Browse files
committed
Add monotonic, perf_counter, get_clock_info for windows
Signed-off-by: snowapril <[email protected]>
1 parent 5f158f2 commit ac7bb4b

File tree

1 file changed

+145
-12
lines changed

1 file changed

+145
-12
lines changed

vm/src/stdlib/time.rs

Lines changed: 145 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,28 +90,28 @@ mod time {
9090
Ok(Date::now() / 1000.0)
9191
}
9292

93-
#[cfg(any(windows, target_os = "wasi"))]
93+
#[cfg(target_os = "wasi")]
9494
#[pyfunction]
9595
fn monotonic(vm: &VirtualMachine) -> PyResult<f64> {
9696
// TODO: implement proper monotonic time for other platforms.
9797
Ok(duration_since_system_now(vm)?.as_secs_f64())
9898
}
9999

100-
#[cfg(any(windows, target_os = "wasi"))]
100+
#[cfg(target_os = "wasi")]
101101
#[pyfunction]
102102
fn monotonic_ns(vm: &VirtualMachine) -> PyResult<u128> {
103103
// TODO: implement proper monotonic time for other platforms.
104104
Ok(duration_since_system_now(vm)?.as_nanos())
105105
}
106106

107-
#[cfg(any(windows, target_os = "wasi"))]
107+
#[cfg(target_os = "wasi")]
108108
#[pyfunction]
109109
fn perf_counter(vm: &VirtualMachine) -> PyResult<f64> {
110110
// TODO: implement proper monotonic time for other platforms.
111111
Ok(duration_since_system_now(vm)?.as_secs_f64())
112112
}
113113

114-
#[cfg(any(windows, target_os = "wasi"))]
114+
#[cfg(target_os = "wasi")]
115115
#[pyfunction]
116116
fn perf_counter_ns(vm: &VirtualMachine) -> PyResult<u128> {
117117
// TODO: implement proper monotonic time for other platforms.
@@ -244,15 +244,16 @@ mod time {
244244
Ok(get_thread_time(vm)?.as_nanos() as u64)
245245
}
246246

247+
#[cfg(any(windows, all(target_arch = "wasm32", not(target_os = "unknown"))))]
248+
pub(super) fn time_muldiv(ticks: i64, mul: i64, div: i64) -> u64 {
249+
let intpart = ticks / div;
250+
let ticks = ticks % div;
251+
let remaining = (ticks * mul) / div;
252+
(intpart * mul + remaining) as u64
253+
}
254+
247255
#[cfg(all(target_arch = "wasm32", not(target_os = "unknown")))]
248256
fn get_process_time(vm: &VirtualMachine) -> PyResult<std::time::Duration> {
249-
fn time_muldiv(ticks: i64, mul: i64, div: i64) -> u64 {
250-
let intpart = ticks / div;
251-
let ticks = ticks % div;
252-
let remaining = (ticks * mul) / div;
253-
(intpart * mul + remaining) as u64
254-
}
255-
256257
let t: libc::tms = unsafe {
257258
let mut t = std::mem::MaybeUninit::uninit();
258259
if libc::times(t.as_mut_ptr()) == -1 {
@@ -684,12 +685,19 @@ mod unix {
684685
#[cfg(windows)]
685686
#[pymodule(name = "time")]
686687
mod windows {
687-
use crate::{PyResult, VirtualMachine};
688+
use super::{time_muldiv, MS_TO_NS, SEC_TO_NS};
689+
use crate::{
690+
builtins::{PyNamespace, PyStrRef},
691+
stdlib::os::errno_err,
692+
PyRef, PyResult, VirtualMachine,
693+
};
688694
use std::time::Duration;
689695
use winapi::shared::{minwindef::FILETIME, ntdef::ULARGE_INTEGER};
690696
use winapi::um::processthreadsapi::{
691697
GetCurrentProcess, GetCurrentThread, GetProcessTimes, GetThreadTimes,
692698
};
699+
use winapi::um::profileapi::{QueryPerformanceCounter, QueryPerformanceFrequency};
700+
use winapi::um::sysinfoapi::{GetSystemTimeAdjustment, GetTickCount64};
693701

694702
fn u64_from_filetime(time: FILETIME) -> u64 {
695703
unsafe {
@@ -704,6 +712,131 @@ mod windows {
704712
}
705713
}
706714

715+
fn win_perf_counter_frequency(vm: &VirtualMachine) -> PyResult<i64> {
716+
let freq = unsafe {
717+
let mut freq = std::mem::MaybeUninit::uninit();
718+
if QueryPerformanceFrequency(freq.as_mut_ptr()) == 0 {
719+
return Err(errno_err(vm));
720+
}
721+
freq.assume_init()
722+
};
723+
let frequency = unsafe { freq.QuadPart() };
724+
725+
if *frequency < 1 {
726+
Err(vm.new_runtime_error("invalid QueryPerformanceFrequency".to_owned()))
727+
} else if *frequency > i64::MAX / SEC_TO_NS {
728+
Err(vm.new_overflow_error("QueryPerformanceFrequency is too large".to_owned()))
729+
} else {
730+
Ok(*frequency)
731+
}
732+
}
733+
734+
fn global_frequency(vm: &VirtualMachine) -> PyResult<i64> {
735+
rustpython_common::static_cell! {
736+
static FREQUENCY: PyResult<i64>;
737+
};
738+
FREQUENCY
739+
.get_or_init(|| win_perf_counter_frequency(vm))
740+
.clone()
741+
}
742+
743+
fn win_perf_counter(vm: &VirtualMachine) -> PyResult<Duration> {
744+
let now = unsafe {
745+
let mut performance_count = std::mem::MaybeUninit::uninit();
746+
QueryPerformanceCounter(performance_count.as_mut_ptr());
747+
performance_count.assume_init()
748+
};
749+
750+
let ticks = unsafe { now.QuadPart() };
751+
Ok(Duration::from_nanos(time_muldiv(
752+
*ticks,
753+
SEC_TO_NS,
754+
global_frequency(vm)?,
755+
)))
756+
}
757+
758+
fn get_system_time_adjustment(vm: &VirtualMachine) -> PyResult<u32> {
759+
let mut _time_adjustment = std::mem::MaybeUninit::uninit();
760+
let mut time_increment = std::mem::MaybeUninit::uninit();
761+
let mut _is_time_adjustment_disabled = std::mem::MaybeUninit::uninit();
762+
let time_increment = unsafe {
763+
if GetSystemTimeAdjustment(
764+
_time_adjustment.as_mut_ptr(),
765+
time_increment.as_mut_ptr(),
766+
_is_time_adjustment_disabled.as_mut_ptr(),
767+
) == 0
768+
{
769+
return Err(errno_err(vm));
770+
}
771+
time_increment.assume_init()
772+
};
773+
Ok(time_increment)
774+
}
775+
776+
fn get_monotonic_clock(vm: &VirtualMachine) -> PyResult<Duration> {
777+
let ticks = unsafe { GetTickCount64() };
778+
779+
Ok(Duration::from_nanos(
780+
(ticks as i64).checked_mul(MS_TO_NS).ok_or_else(|| {
781+
vm.new_overflow_error("timestamp too large to convert to i64".to_owned())
782+
})? as u64,
783+
))
784+
}
785+
786+
#[pyfunction]
787+
fn get_clock_info(name: PyStrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
788+
let (adj, imp, mono, res) = match name.as_ref() {
789+
"monotonic" => (
790+
false,
791+
"GetTickCount64()",
792+
true,
793+
get_system_time_adjustment(vm)? as f64 * 1e-7,
794+
),
795+
"perf_counter" => (
796+
false,
797+
"QueryPerformanceCounter()",
798+
true,
799+
1.0 / (global_frequency(vm)? as f64),
800+
),
801+
"process_time" => (false, "GetProcessTimes()", true, 1e-7),
802+
"thread_time" => (false, "GetThreadTimes()", true, 1e-7),
803+
"time" => (
804+
true,
805+
"GetSystemTimeAsFileTime()",
806+
false,
807+
get_system_time_adjustment(vm)? as f64 * 1e-7,
808+
),
809+
_ => return Err(vm.new_value_error("unknown clock".to_owned())),
810+
};
811+
812+
Ok(py_namespace!(vm, {
813+
"implementation" => vm.new_pyobj(imp),
814+
"monotonic" => vm.ctx.new_bool(mono),
815+
"adjustable" => vm.ctx.new_bool(adj),
816+
"resolution" => vm.ctx.new_float(res),
817+
}))
818+
}
819+
820+
#[pyfunction]
821+
fn monotonic(vm: &VirtualMachine) -> PyResult<f64> {
822+
Ok(get_monotonic_clock(vm)?.as_secs_f64())
823+
}
824+
825+
#[pyfunction]
826+
fn monotonic_ns(vm: &VirtualMachine) -> PyResult<u128> {
827+
Ok(get_monotonic_clock(vm)?.as_nanos())
828+
}
829+
830+
#[pyfunction]
831+
fn perf_counter(vm: &VirtualMachine) -> PyResult<f64> {
832+
Ok(win_perf_counter(vm)?.as_secs_f64())
833+
}
834+
835+
#[pyfunction]
836+
fn perf_counter_ns(vm: &VirtualMachine) -> PyResult<u128> {
837+
Ok(win_perf_counter(vm)?.as_nanos())
838+
}
839+
707840
pub(super) fn get_thread_time(vm: &VirtualMachine) -> PyResult<Duration> {
708841
let (kernel_time, user_time) = unsafe {
709842
let mut _creation_time = std::mem::MaybeUninit::uninit();

0 commit comments

Comments
 (0)