Skip to main content

wasmtime_wasi/
clocks.rs

1use std::error::Error;
2use std::fmt;
3use std::time::{Duration, Instant, SystemTime};
4use wasmtime::component::{HasData, ResourceTable};
5
6/// A helper struct which implements [`HasData`] for the `wasi:clocks` APIs.
7///
8/// This can be useful when directly calling `add_to_linker` functions directly,
9/// such as [`wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker`] as
10/// the `D` type parameter. See [`HasData`] for more information about the type
11/// parameter's purpose.
12///
13/// When using this type you can skip the [`WasiClocksView`] trait, for
14/// example.
15///
16/// [`wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker`]: crate::p2::bindings::clocks::monotonic_clock::add_to_linker
17///
18/// # Examples
19///
20/// ```
21/// use wasmtime::component::{Linker, ResourceTable};
22/// use wasmtime::{Engine, Result};
23/// use wasmtime_wasi::clocks::*;
24///
25/// struct MyStoreState {
26///     table: ResourceTable,
27///     clocks: WasiClocksCtx,
28/// }
29///
30/// fn main() -> Result<()> {
31///     let engine = Engine::default();
32///     let mut linker = Linker::new(&engine);
33///
34///     wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker::<MyStoreState, WasiClocks>(
35///         &mut linker,
36///         |state| WasiClocksCtxView {
37///             table: &mut state.table,
38///             ctx: &mut state.clocks,
39///         },
40///     )?;
41///     Ok(())
42/// }
43/// ```
44pub struct WasiClocks;
45
46impl HasData for WasiClocks {
47    type Data<'a> = WasiClocksCtxView<'a>;
48}
49
50pub struct WasiClocksCtx {
51    pub(crate) wall_clock: Box<dyn HostWallClock + Send>,
52    pub(crate) monotonic_clock: Box<dyn HostMonotonicClock + Send>,
53}
54
55impl Default for WasiClocksCtx {
56    fn default() -> Self {
57        Self {
58            wall_clock: wall_clock(),
59            monotonic_clock: monotonic_clock(),
60        }
61    }
62}
63
64pub trait WasiClocksView: Send {
65    fn clocks(&mut self) -> WasiClocksCtxView<'_>;
66}
67
68pub struct WasiClocksCtxView<'a> {
69    pub ctx: &'a mut WasiClocksCtx,
70    pub table: &'a mut ResourceTable,
71}
72
73pub trait HostWallClock: Send {
74    fn resolution(&self) -> Duration;
75    fn now(&self) -> Duration;
76}
77
78pub trait HostMonotonicClock: Send {
79    fn resolution(&self) -> u64;
80    fn now(&self) -> u64;
81}
82
83#[derive(Default)]
84pub struct WallClock;
85
86impl WallClock {
87    pub fn new() -> Self {
88        Self
89    }
90}
91
92impl HostWallClock for WallClock {
93    fn resolution(&self) -> Duration {
94        #[cfg(unix)]
95        {
96            let res = rustix::time::clock_getres(rustix::time::ClockId::Realtime);
97            Duration::new(
98                res.tv_sec.try_into().unwrap(),
99                res.tv_nsec.try_into().unwrap(),
100            )
101        }
102        #[cfg(windows)]
103        {
104            // According to [this blog post], the system timer resolution
105            // is 55ms or 10ms. Use the more conservative of the two.
106            //
107            // [this blog post]: https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057
108            Duration::new(0, 55_000_000)
109        }
110    }
111
112    fn now(&self) -> Duration {
113        // WASI defines wall clocks to return "Unix time".
114        SystemTime::now()
115            .duration_since(SystemTime::UNIX_EPOCH)
116            .unwrap()
117    }
118}
119
120pub struct MonotonicClock {
121    /// The `Instant` this clock was created. All returned times are
122    /// durations since that time.
123    initial: Instant,
124}
125
126impl Default for MonotonicClock {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132impl MonotonicClock {
133    pub fn new() -> Self {
134        Self {
135            initial: Instant::now(),
136        }
137    }
138}
139
140impl HostMonotonicClock for MonotonicClock {
141    fn resolution(&self) -> u64 {
142        #[cfg(unix)]
143        {
144            let res = rustix::time::clock_getres(rustix::time::ClockId::Monotonic);
145            u64::try_from(res.tv_sec).unwrap() * 1_000_000_000 + u64::try_from(res.tv_nsec).unwrap()
146        }
147        #[cfg(windows)]
148        {
149            use windows_sys::Win32::System::Performance::QueryPerformanceFrequency;
150
151            unsafe {
152                let mut frequency = 0;
153                if QueryPerformanceFrequency(&mut frequency) == 0 {
154                    panic!(
155                        "QueryPerformanceFrequency failed: {}",
156                        std::io::Error::last_os_error()
157                    );
158                }
159                1_000_000_000 / u64::try_from(frequency).unwrap()
160            }
161        }
162    }
163
164    fn now(&self) -> u64 {
165        // Unwrap here and in `resolution` above; a `u64` is wide enough to
166        // hold over 584 years of nanoseconds.
167        Instant::now()
168            .duration_since(self.initial)
169            .as_nanos()
170            .try_into()
171            .unwrap()
172    }
173}
174
175pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
176    Box::new(MonotonicClock::default())
177}
178
179pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
180    Box::new(WallClock::default())
181}
182
183pub(crate) struct Datetime {
184    pub seconds: i64,
185    pub nanoseconds: u32,
186}
187
188impl TryFrom<SystemTime> for Datetime {
189    type Error = DatetimeError;
190
191    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
192        let epoch = SystemTime::UNIX_EPOCH;
193
194        if time >= epoch {
195            let duration = time.duration_since(epoch)?;
196            Ok(Self {
197                seconds: duration.as_secs().try_into()?,
198                nanoseconds: duration.subsec_nanos(),
199            })
200        } else {
201            let duration = epoch.duration_since(time)?;
202            Ok(Self {
203                seconds: -duration.as_secs().try_into()?,
204                nanoseconds: duration.subsec_nanos(),
205            })
206        }
207    }
208}
209
210#[derive(Debug)]
211pub struct DatetimeError;
212
213impl fmt::Display for DatetimeError {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        f.write_str("couldn't represent time as a WASI `Datetime`")
216    }
217}
218
219impl Error for DatetimeError {}
220
221impl From<std::time::SystemTimeError> for DatetimeError {
222    fn from(_: std::time::SystemTimeError) -> Self {
223        DatetimeError
224    }
225}
226
227impl From<std::num::TryFromIntError> for DatetimeError {
228    fn from(_: std::num::TryFromIntError) -> Self {
229        DatetimeError
230    }
231}