From cab44a2ac3b5c4f63fec895f5638b944ec4c9b6d Mon Sep 17 00:00:00 2001 From: jfh Date: Tue, 26 Oct 2021 12:12:37 +0300 Subject: [PATCH 1/4] Add clock_gettime, clock_settime, clock_getres. --- Lib/test/test_time.py | 2 + vm/src/stdlib/time.rs | 153 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 142 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 78fce40ca..bb67ceef9 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -541,6 +541,8 @@ class TimeTestCase(unittest.TestCase): self.assertTrue(info.monotonic) self.assertFalse(info.adjustable) + # TODO: RUSTPYTHON fix time.monotonic, currently calls System::now (i.e CLOCK_REALTIME) + @unittest.expectedFailure @unittest.skipUnless(hasattr(time, 'clock_settime'), 'need time.clock_settime') def test_monotonic_settime(self): diff --git a/vm/src/stdlib/time.rs b/vm/src/stdlib/time.rs index 6527c4d79..4959a42e9 100644 --- a/vm/src/stdlib/time.rs +++ b/vm/src/stdlib/time.rs @@ -372,23 +372,158 @@ mod time { #[cfg(unix)] #[pymodule(name = "time")] mod unix { - use crate::{PyResult, VirtualMachine}; + #[allow(unused_imports)] + use super::{SEC_TO_NS, US_TO_NS}; + use crate::{ + builtins::{try_bigint_to_f64, PyFloat, PyIntRef}, + utils::Either, + PyRef, PyResult, VirtualMachine, + }; use std::time::Duration; #[cfg(target_os = "solaris")] #[pyattr] use libc::CLOCK_HIGHRES; + #[cfg(not(target_os = "redox"))] + use libc::CLOCK_PROCESS_CPUTIME_ID; #[cfg(target_os = "linux")] #[pyattr] use libc::{CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW, CLOCK_TAI}; #[pyattr] - use libc::{ - CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID, - }; + use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID}; + #[cfg(target_os = "redox")] + // TODO: will be upstreamed to libc sometime soon + const CLOCK_PROCESS_CPUTIME_ID: libc::clockid_t = 2; #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] #[pyattr] use libc::{CLOCK_PROF, CLOCK_UPTIME}; + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + fn get_clock_time(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + let mut timespec = std::mem::MaybeUninit::uninit(); + let ts: libc::timespec = unsafe { + if libc::clock_gettime(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 { + return Err(vm.new_os_error("Invalid argument".to_owned())); + } + timespec.assume_init() + }; + Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)) + } + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + fn get_clock_time(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_not_implemented_error("clock_gettime unsupported on this system".to_owned())) + } + + #[pyfunction] + fn clock_gettime(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + get_clock_time(clk_id, vm).map(|d| d.as_secs_f64()) + } + + #[pyfunction] + fn clock_gettime_ns(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + get_clock_time(clk_id, vm).map(|d| d.as_nanos()) + } + + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn clock_getres(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + let mut timespec = std::mem::MaybeUninit::uninit(); + let ts: libc::timespec = unsafe { + if libc::clock_getres(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 { + return Err(vm.new_os_error("Invalid argument".to_owned())); + } + timespec.assume_init() + }; + Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32).as_secs_f64()) + } + + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + #[pyfunction] + fn clock_getres(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_not_implemented_error("clock_getres unsupported on this system".to_owned())) + } + + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + fn set_clock_time( + clk_id: PyIntRef, + timespec: libc::timespec, + vm: &VirtualMachine, + ) -> PyResult<()> { + let res = unsafe { libc::clock_settime(clk_id.try_to_primitive(vm)?, ×pec) }; + if res == -1 { + return Err(vm.new_os_error("Invalid argument".to_owned())); + } + Ok(()) + } + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + fn set_clock_time(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<()> { + Err(vm.new_not_implemented_error("clock_settime unsupported on this system".to_owned())) + } + + #[pyfunction] + fn clock_settime( + clk_id: PyIntRef, + time: Either, PyIntRef>, + vm: &VirtualMachine, + ) -> PyResult<()> { + let time = match time { + Either::A(f) => f.to_f64(), + Either::B(z) => try_bigint_to_f64(z.as_bigint(), vm)?, + }; + let nanos = time.fract() * (SEC_TO_NS as f64); + let ts = libc::timespec { + tv_sec: time.floor() as libc::time_t, + tv_nsec: nanos as _, + }; + set_clock_time(clk_id, ts, vm) + } + + #[pyfunction] + fn clock_settime_ns(clk_id: PyIntRef, time: PyIntRef, vm: &VirtualMachine) -> PyResult<()> { + let time: i64 = time.try_to_primitive(vm)?; + let ts = libc::timespec { + tv_sec: time / SEC_TO_NS as libc::time_t, + tv_nsec: time.rem_euclid(SEC_TO_NS) as _, + }; + set_clock_time(clk_id, ts, vm) + } + #[pyfunction] fn sleep(dur: Duration, vm: &VirtualMachine) -> PyResult<()> { // this is basically std::thread::sleep, but that catches interrupts and we don't want to; @@ -418,7 +553,7 @@ mod unix { pub(super) fn get_thread_time(vm: &VirtualMachine) -> PyResult { let time: libc::timespec = unsafe { let mut time = std::mem::MaybeUninit::uninit(); - if libc::clock_gettime(libc::CLOCK_THREAD_CPUTIME_ID, time.as_mut_ptr()) == -1 { + if libc::clock_gettime(CLOCK_THREAD_CPUTIME_ID, time.as_mut_ptr()) == -1 { return Err(vm.new_os_error("Failed to get clock time".to_owned())); } time.assume_init() @@ -441,12 +576,6 @@ mod unix { ))] pub(super) fn get_process_time(vm: &VirtualMachine) -> PyResult { let time: libc::timespec = unsafe { - #[cfg(not(target_os = "redox"))] - use libc::CLOCK_PROCESS_CPUTIME_ID; - #[cfg(target_os = "redox")] - // TODO: will be upstreamed to libc sometime soon - const CLOCK_PROCESS_CPUTIME_ID: libc::clockid_t = 2; - let mut time = std::mem::MaybeUninit::uninit(); if libc::clock_gettime(CLOCK_PROCESS_CPUTIME_ID, time.as_mut_ptr()) == -1 { return Err(vm.new_os_error("Failed to get clock time".to_owned())); @@ -464,8 +593,6 @@ mod unix { ))] pub(super) fn get_process_time(vm: &VirtualMachine) -> PyResult { fn from_timeval(tv: libc::timeval, vm: &VirtualMachine) -> PyResult { - use super::decl::{SEC_TO_NS, US_TO_NS}; - (|tv: libc::timeval| { let t = tv.tv_sec.checked_mul(SEC_TO_NS)?; let u = (tv.tv_usec as i64).checked_mul(US_TO_NS)?; From 1817fd6565d1c4042b7f670e962cafce3ca90a9d Mon Sep 17 00:00:00 2001 From: jfh Date: Wed, 27 Oct 2021 10:36:10 +0300 Subject: [PATCH 2/4] Add get_clock_info. --- Lib/test/test_time.py | 10 -------- vm/src/stdlib/time.rs | 59 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index bb67ceef9..61460b0ea 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -60,8 +60,6 @@ class TimeTestCase(unittest.TestCase): time.timezone time.tzname - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'get_clock_info' - @unittest.expectedFailure def test_time(self): time.time() info = time.get_clock_info('time') @@ -476,8 +474,6 @@ class TimeTestCase(unittest.TestCase): pass self.assertEqual(time.strftime('%Z', tt), tzname) - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'get_clock_info' - @unittest.expectedFailure def test_monotonic(self): # monotonic() should not go backward times = [time.monotonic() for n in range(100)] @@ -504,8 +500,6 @@ class TimeTestCase(unittest.TestCase): def test_perf_counter(self): time.perf_counter() - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'get_clock_info' - @unittest.expectedFailure def test_process_time(self): # process_time() should not include time spend during a sleep start = time.process_time() @@ -519,8 +513,6 @@ class TimeTestCase(unittest.TestCase): self.assertTrue(info.monotonic) self.assertFalse(info.adjustable) - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'get_clock_info' - @unittest.expectedFailure def test_thread_time(self): if not hasattr(time, 'thread_time'): if sys.platform.startswith(('linux', 'win')): @@ -579,8 +571,6 @@ class TimeTestCase(unittest.TestCase): self.assertRaises(ValueError, time.localtime, float("nan")) self.assertRaises(ValueError, time.ctime, float("nan")) - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'get_clock_info' - @unittest.expectedFailure def test_get_clock_info(self): clocks = ['monotonic', 'perf_counter', 'process_time', 'time'] diff --git a/vm/src/stdlib/time.rs b/vm/src/stdlib/time.rs index 4959a42e9..3d468c588 100644 --- a/vm/src/stdlib/time.rs +++ b/vm/src/stdlib/time.rs @@ -375,7 +375,7 @@ mod unix { #[allow(unused_imports)] use super::{SEC_TO_NS, US_TO_NS}; use crate::{ - builtins::{try_bigint_to_f64, PyFloat, PyIntRef}, + builtins::{try_bigint_to_f64, PyFloat, PyIntRef, PyNamespace, PyStrRef}, utils::Either, PyRef, PyResult, VirtualMachine, }; @@ -524,6 +524,63 @@ mod unix { set_clock_time(clk_id, ts, vm) } + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn get_clock_info(name: PyStrRef, vm: &VirtualMachine) -> PyResult> { + let (adj, imp, mono, res) = match name.as_ref() { + "monotonic" | "perf_counter" => ( + false, + "time.clock_gettime(CLOCK_MONOTONIC)", + true, + clock_getres(vm.ctx.new_int(CLOCK_MONOTONIC), vm)?, + ), + "process_time" => ( + false, + "time.clock_gettime(CLOCK_PROCESS_CPUTIME_ID)", + true, + clock_getres(vm.ctx.new_int(CLOCK_PROCESS_CPUTIME_ID), vm)?, + ), + "thread_time" => ( + false, + "time.clock_gettime(CLOCK_THREAD_CPUTIME_ID)", + true, + clock_getres(vm.ctx.new_int(CLOCK_THREAD_CPUTIME_ID), vm)?, + ), + "time" => ( + true, + "time.clock_gettime(CLOCK_REALTIME)", + false, + clock_getres(vm.ctx.new_int(CLOCK_REALTIME), vm)?, + ), + _ => return Err(vm.new_value_error("unknown clock".to_owned())), + }; + + Ok(py_namespace!(vm, { + "implementation" => vm.new_pyobj(imp), + "monotonic" => vm.ctx.new_bool(mono), + "adjustable" => vm.ctx.new_bool(adj), + "resolution" => vm.ctx.new_float(res), + })) + } + + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + #[pyfunction] + fn get_clock_info(_name: PyStrRef, vm: &VirtualMachine) -> PyResult { + Err(vm.new_not_implemented_error("get_clock_info unsupported on this system".to_owned())) + } + #[pyfunction] fn sleep(dur: Duration, vm: &VirtualMachine) -> PyResult<()> { // this is basically std::thread::sleep, but that catches interrupts and we don't want to; From ee77f3c3327e43ca41e568e1e365b8781375aacf Mon Sep 17 00:00:00 2001 From: jfh Date: Wed, 27 Oct 2021 11:54:58 +0300 Subject: [PATCH 3/4] Add monotonic_ns, perf_counter, perf_counter_ns. --- Lib/test/test_time.py | 4 -- vm/src/stdlib/time.rs | 96 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index 61460b0ea..abda4adcb 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -66,8 +66,6 @@ class TimeTestCase(unittest.TestCase): self.assertFalse(info.monotonic) self.assertTrue(info.adjustable) - # TODO: RUSTPYTHON, AttributeError: module 'time' has no attribute 'monotonic_ns' - @unittest.expectedFailure def test_time_ns_type(self): def check_ns(sec, ns): self.assertIsInstance(ns, int) @@ -533,8 +531,6 @@ class TimeTestCase(unittest.TestCase): self.assertTrue(info.monotonic) self.assertFalse(info.adjustable) - # TODO: RUSTPYTHON fix time.monotonic, currently calls System::now (i.e CLOCK_REALTIME) - @unittest.expectedFailure @unittest.skipUnless(hasattr(time, 'clock_settime'), 'need time.clock_settime') def test_monotonic_settime(self): diff --git a/vm/src/stdlib/time.rs b/vm/src/stdlib/time.rs index 3d468c588..a053c7750 100644 --- a/vm/src/stdlib/time.rs +++ b/vm/src/stdlib/time.rs @@ -90,12 +90,58 @@ mod time { Ok(Date::now() / 1000.0) } + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] #[pyfunction] fn monotonic(vm: &VirtualMachine) -> PyResult { - // TODO: implement proper monotonic time! + // TODO: implement proper monotonic time for other platforms. Ok(duration_since_system_now(vm)?.as_secs_f64()) } + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + #[pyfunction] + fn monotonic_ns(vm: &VirtualMachine) -> PyResult { + // TODO: implement proper monotonic time for other platforms. + Ok(duration_since_system_now(vm)?.as_nanos()) + } + + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + #[pyfunction] + fn perf_counter(vm: &VirtualMachine) -> PyResult { + // TODO: implement proper monotonic time for other platforms. + Ok(duration_since_system_now(vm)?.as_secs_f64()) + } + + #[cfg(not(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + )))] + #[pyfunction] + fn perf_counter_ns(vm: &VirtualMachine) -> PyResult { + // TODO: implement proper monotonic time for other platforms. + Ok(duration_since_system_now(vm)?.as_nanos()) + } + fn pyobj_to_naive_date_time( value: Either, vm: &VirtualMachine, @@ -581,6 +627,54 @@ mod unix { Err(vm.new_not_implemented_error("get_clock_info unsupported on this system".to_owned())) } + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn monotonic(vm: &VirtualMachine) -> PyResult { + clock_gettime(vm.ctx.new_int(CLOCK_MONOTONIC), vm) + } + + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn monotonic_ns(vm: &VirtualMachine) -> PyResult { + clock_gettime_ns(vm.ctx.new_int(CLOCK_MONOTONIC), vm) + } + + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn perf_counter(vm: &VirtualMachine) -> PyResult { + clock_gettime(vm.ctx.new_int(CLOCK_MONOTONIC), vm) + } + + #[cfg(any( + target_os = "macos", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + ))] + #[pyfunction] + fn perf_counter_ns(vm: &VirtualMachine) -> PyResult { + clock_gettime_ns(vm.ctx.new_int(CLOCK_MONOTONIC), vm) + } + #[pyfunction] fn sleep(dur: Duration, vm: &VirtualMachine) -> PyResult<()> { // this is basically std::thread::sleep, but that catches interrupts and we don't want to; From c6ac24c2f24d0a1bf37e88776078f02a2d8348a8 Mon Sep 17 00:00:00 2001 From: jfh Date: Wed, 27 Oct 2021 13:51:55 +0300 Subject: [PATCH 4/4] Clean up cfgs, handle 32bit timespec creation, skip tests for windows. --- Lib/test/test_time.py | 12 +++ vm/src/stdlib/time.rs | 190 ++++++++++++------------------------------ 2 files changed, 64 insertions(+), 138 deletions(-) diff --git a/Lib/test/test_time.py b/Lib/test/test_time.py index abda4adcb..7ee6f8234 100644 --- a/Lib/test/test_time.py +++ b/Lib/test/test_time.py @@ -60,12 +60,16 @@ class TimeTestCase(unittest.TestCase): time.timezone time.tzname + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement get_clock_info for Windows.") def test_time(self): time.time() info = time.get_clock_info('time') self.assertFalse(info.monotonic) self.assertTrue(info.adjustable) + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement monotonic_ns for Windows.") def test_time_ns_type(self): def check_ns(sec, ns): self.assertIsInstance(ns, int) @@ -472,6 +476,8 @@ class TimeTestCase(unittest.TestCase): pass self.assertEqual(time.strftime('%Z', tt), tzname) + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement get_clock_info for Windows.") def test_monotonic(self): # monotonic() should not go backward times = [time.monotonic() for n in range(100)] @@ -498,6 +504,8 @@ class TimeTestCase(unittest.TestCase): def test_perf_counter(self): time.perf_counter() + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement get_clock_info for Windows.") def test_process_time(self): # process_time() should not include time spend during a sleep start = time.process_time() @@ -511,6 +519,8 @@ class TimeTestCase(unittest.TestCase): self.assertTrue(info.monotonic) self.assertFalse(info.adjustable) + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement get_clock_info for Windows.") def test_thread_time(self): if not hasattr(time, 'thread_time'): if sys.platform.startswith(('linux', 'win')): @@ -567,6 +577,8 @@ class TimeTestCase(unittest.TestCase): self.assertRaises(ValueError, time.localtime, float("nan")) self.assertRaises(ValueError, time.ctime, float("nan")) + # TODO: RUSTPYTHON + @unittest.skipIf(sys.platform == "win32", "Implement get_clock_info for Windows.") def test_get_clock_info(self): clocks = ['monotonic', 'perf_counter', 'process_time', 'time'] diff --git a/vm/src/stdlib/time.rs b/vm/src/stdlib/time.rs index a053c7750..a7d9633dc 100644 --- a/vm/src/stdlib/time.rs +++ b/vm/src/stdlib/time.rs @@ -90,52 +90,28 @@ mod time { Ok(Date::now() / 1000.0) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] + #[cfg(any(windows, target_os = "wasi"))] #[pyfunction] fn monotonic(vm: &VirtualMachine) -> PyResult { // TODO: implement proper monotonic time for other platforms. Ok(duration_since_system_now(vm)?.as_secs_f64()) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] + #[cfg(any(windows, target_os = "wasi"))] #[pyfunction] fn monotonic_ns(vm: &VirtualMachine) -> PyResult { // TODO: implement proper monotonic time for other platforms. Ok(duration_since_system_now(vm)?.as_nanos()) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] + #[cfg(any(windows, target_os = "wasi"))] #[pyfunction] fn perf_counter(vm: &VirtualMachine) -> PyResult { // TODO: implement proper monotonic time for other platforms. Ok(duration_since_system_now(vm)?.as_secs_f64()) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] + #[cfg(any(windows, target_os = "wasi"))] #[pyfunction] fn perf_counter_ns(vm: &VirtualMachine) -> PyResult { // TODO: implement proper monotonic time for other platforms. @@ -251,8 +227,8 @@ mod time { target_os = "dragonfly", target_os = "freebsd", target_os = "linux", - target_os = "openbsd", - target_os = "solaris", + target_os = "fuchsia", + target_os = "emscripten", )))] fn get_thread_time(vm: &VirtualMachine) -> PyResult { Err(vm.new_not_implemented_error("thread time unsupported in this system".to_owned())) @@ -420,8 +396,10 @@ mod time { mod unix { #[allow(unused_imports)] use super::{SEC_TO_NS, US_TO_NS}; + #[cfg_attr(target_os = "macos", allow(unused_imports))] use crate::{ builtins::{try_bigint_to_f64, PyFloat, PyIntRef, PyNamespace, PyStrRef}, + stdlib::os, utils::Either, PyRef, PyResult, VirtualMachine, }; @@ -430,47 +408,42 @@ mod unix { #[cfg(target_os = "solaris")] #[pyattr] use libc::CLOCK_HIGHRES; - #[cfg(not(target_os = "redox"))] + #[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", + target_os = "solaris", + target_os = "openbsd", + )))] + #[pyattr] use libc::CLOCK_PROCESS_CPUTIME_ID; + #[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", + target_os = "solaris", + target_os = "openbsd", + target_os = "redox", + )))] + #[pyattr] + use libc::CLOCK_THREAD_CPUTIME_ID; #[cfg(target_os = "linux")] #[pyattr] use libc::{CLOCK_BOOTTIME, CLOCK_MONOTONIC_RAW, CLOCK_TAI}; #[pyattr] - use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_THREAD_CPUTIME_ID}; - #[cfg(target_os = "redox")] - // TODO: will be upstreamed to libc sometime soon - const CLOCK_PROCESS_CPUTIME_ID: libc::clockid_t = 2; - #[cfg(any(target_os = "freebsd", target_os = "openbsd"))] + use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; + #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly"))] #[pyattr] use libc::{CLOCK_PROF, CLOCK_UPTIME}; - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] fn get_clock_time(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { let mut timespec = std::mem::MaybeUninit::uninit(); let ts: libc::timespec = unsafe { if libc::clock_gettime(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 { - return Err(vm.new_os_error("Invalid argument".to_owned())); + return Err(os::errno_err(vm)); } timespec.assume_init() }; Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32)) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] - fn get_clock_time(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { - Err(vm.new_not_implemented_error("clock_gettime unsupported on this system".to_owned())) - } #[pyfunction] fn clock_gettime(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { @@ -482,44 +455,20 @@ mod unix { get_clock_time(clk_id, vm).map(|d| d.as_nanos()) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] + #[cfg(not(target_os = "redox"))] #[pyfunction] fn clock_getres(clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { let mut timespec = std::mem::MaybeUninit::uninit(); let ts: libc::timespec = unsafe { if libc::clock_getres(clk_id.try_to_primitive(vm)?, timespec.as_mut_ptr()) == -1 { - return Err(vm.new_os_error("Invalid argument".to_owned())); + return Err(os::errno_err(vm)); } timespec.assume_init() }; Ok(Duration::new(ts.tv_sec as u64, ts.tv_nsec as u32).as_secs_f64()) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] - #[pyfunction] - fn clock_getres(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult { - Err(vm.new_not_implemented_error("clock_getres unsupported on this system".to_owned())) - } - - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] + #[cfg(not(any(target_os = "macos", target_os = "redox")))] fn set_clock_time( clk_id: PyIntRef, timespec: libc::timespec, @@ -527,21 +476,12 @@ mod unix { ) -> PyResult<()> { let res = unsafe { libc::clock_settime(clk_id.try_to_primitive(vm)?, ×pec) }; if res == -1 { - return Err(vm.new_os_error("Invalid argument".to_owned())); + return Err(os::errno_err(vm)); } Ok(()) } - #[cfg(not(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - )))] - fn set_clock_time(_clk_id: PyIntRef, vm: &VirtualMachine) -> PyResult<()> { - Err(vm.new_not_implemented_error("clock_settime unsupported on this system".to_owned())) - } + #[cfg(not(any(target_os = "macos", target_os = "redox")))] #[pyfunction] fn clock_settime( clk_id: PyIntRef, @@ -560,21 +500,25 @@ mod unix { set_clock_time(clk_id, ts, vm) } + #[cfg(not(any(target_os = "macos", target_os = "redox")))] #[pyfunction] fn clock_settime_ns(clk_id: PyIntRef, time: PyIntRef, vm: &VirtualMachine) -> PyResult<()> { - let time: i64 = time.try_to_primitive(vm)?; + let time: libc::time_t = time.try_to_primitive(vm)?; let ts = libc::timespec { - tv_sec: time / SEC_TO_NS as libc::time_t, - tv_nsec: time.rem_euclid(SEC_TO_NS) as _, + tv_sec: time / (SEC_TO_NS as libc::time_t), + tv_nsec: time.rem_euclid(SEC_TO_NS as libc::time_t) as _, }; set_clock_time(clk_id, ts, vm) } + // Requires all CLOCK constants available and clock_getres #[cfg(any( target_os = "macos", target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "fuchsia", + target_os = "emscripten", target_os = "linux", ))] #[pyfunction] @@ -620,6 +564,8 @@ mod unix { target_os = "android", target_os = "dragonfly", target_os = "freebsd", + target_os = "fuchsia", + target_os = "emscripten", target_os = "linux", )))] #[pyfunction] @@ -627,49 +573,21 @@ mod unix { Err(vm.new_not_implemented_error("get_clock_info unsupported on this system".to_owned())) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] #[pyfunction] fn monotonic(vm: &VirtualMachine) -> PyResult { clock_gettime(vm.ctx.new_int(CLOCK_MONOTONIC), vm) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] #[pyfunction] fn monotonic_ns(vm: &VirtualMachine) -> PyResult { clock_gettime_ns(vm.ctx.new_int(CLOCK_MONOTONIC), vm) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] #[pyfunction] fn perf_counter(vm: &VirtualMachine) -> PyResult { clock_gettime(vm.ctx.new_int(CLOCK_MONOTONIC), vm) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - ))] #[pyfunction] fn perf_counter_ns(vm: &VirtualMachine) -> PyResult { clock_gettime_ns(vm.ctx.new_int(CLOCK_MONOTONIC), vm) @@ -693,14 +611,12 @@ mod unix { Ok(()) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", + #[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", target_os = "openbsd", - ))] + target_os = "redox" + )))] pub(super) fn get_thread_time(vm: &VirtualMachine) -> PyResult { let time: libc::timespec = unsafe { let mut time = std::mem::MaybeUninit::uninit(); @@ -717,14 +633,12 @@ mod unix { Ok(Duration::from_nanos(unsafe { libc::gethrvtime() })) } - #[cfg(any( - target_os = "macos", - target_os = "android", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "linux", - target_os = "redox", - ))] + #[cfg(not(any( + target_os = "illumos", + target_os = "netbsd", + target_os = "solaris", + target_os = "openbsd", + )))] pub(super) fn get_process_time(vm: &VirtualMachine) -> PyResult { let time: libc::timespec = unsafe { let mut time = std::mem::MaybeUninit::uninit();