Skip to main content

wasmtime_wasi/
clocks.rs

1use crate::{NamedId, WasiCtxNamedView};
2use std::error::Error;
3use std::fmt;
4use std::marker;
5use std::time::{Duration, Instant, SystemTime};
6use wasmtime::component::{HasData, ResourceTable};
7
8/// A helper struct which implements [`HasData`] for the `wasi:clocks` APIs.
9///
10/// This can be useful when directly calling `add_to_linker` functions directly,
11/// such as [`wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker`] as
12/// the `D` type parameter. See [`HasData`] for more information about the type
13/// parameter's purpose.
14///
15/// When using this type you can skip the [`WasiClocksView`] trait, for
16/// example.
17///
18/// [`wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker`]: crate::p2::bindings::clocks::monotonic_clock::add_to_linker
19///
20/// # Examples
21///
22/// ```
23/// use wasmtime::component::{Linker, ResourceTable};
24/// use wasmtime::{Engine, Result};
25/// use wasmtime_wasi::clocks::*;
26///
27/// struct MyStoreState {
28///     table: ResourceTable,
29///     clocks: WasiClocksCtx,
30/// }
31///
32/// fn main() -> Result<()> {
33///     let engine = Engine::default();
34///     let mut linker = Linker::new(&engine);
35///
36///     wasmtime_wasi::p2::bindings::clocks::monotonic_clock::add_to_linker::<MyStoreState, WasiClocks>(
37///         &mut linker,
38///         |state| WasiClocksCtxView {
39///             table: &mut state.table,
40///             ctx: &mut state.clocks,
41///         },
42///     )?;
43///     Ok(())
44/// }
45/// ```
46pub struct WasiClocks;
47
48impl HasData for WasiClocks {
49    type Data<'a> = WasiClocksCtxView<'a>;
50}
51
52pub struct WasiClocksCtx {
53    pub(crate) wall_clock: Box<dyn HostWallClock + Send>,
54    pub(crate) monotonic_clock: Box<dyn HostMonotonicClock + Send>,
55}
56
57impl Default for WasiClocksCtx {
58    fn default() -> Self {
59        Self {
60            wall_clock: wall_clock(),
61            monotonic_clock: monotonic_clock(),
62        }
63    }
64}
65
66pub trait WasiClocksView: Send {
67    fn clocks(&mut self) -> WasiClocksCtxView<'_>;
68}
69
70pub struct WasiClocksCtxView<'a> {
71    pub ctx: &'a mut WasiClocksCtx,
72    pub table: &'a mut ResourceTable,
73}
74
75pub trait HostWallClock: Send {
76    fn resolution(&self) -> Duration;
77    fn now(&self) -> Duration;
78}
79
80pub trait HostMonotonicClock: Send {
81    fn resolution(&self) -> u64;
82    fn now(&self) -> u64;
83}
84
85#[derive(Default)]
86pub struct WallClock;
87
88impl WallClock {
89    pub fn new() -> Self {
90        Self
91    }
92}
93
94impl HostWallClock for WallClock {
95    fn resolution(&self) -> Duration {
96        #[cfg(unix)]
97        {
98            let res = rustix::time::clock_getres(rustix::time::ClockId::Realtime);
99            Duration::new(
100                res.tv_sec.try_into().unwrap(),
101                res.tv_nsec.try_into().unwrap(),
102            )
103        }
104        #[cfg(windows)]
105        {
106            // According to [this blog post], the system timer resolution
107            // is 55ms or 10ms. Use the more conservative of the two.
108            //
109            // [this blog post]: https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057
110            Duration::new(0, 55_000_000)
111        }
112    }
113
114    fn now(&self) -> Duration {
115        // WASI defines wall clocks to return "Unix time".
116        SystemTime::now()
117            .duration_since(SystemTime::UNIX_EPOCH)
118            .unwrap()
119    }
120}
121
122pub struct MonotonicClock {
123    /// The `Instant` this clock was created. All returned times are
124    /// durations since that time.
125    initial: Instant,
126}
127
128impl Default for MonotonicClock {
129    fn default() -> Self {
130        Self::new()
131    }
132}
133
134impl MonotonicClock {
135    pub fn new() -> Self {
136        Self {
137            initial: Instant::now(),
138        }
139    }
140}
141
142impl HostMonotonicClock for MonotonicClock {
143    fn resolution(&self) -> u64 {
144        #[cfg(unix)]
145        {
146            let res = rustix::time::clock_getres(rustix::time::ClockId::Monotonic);
147            u64::try_from(res.tv_sec).unwrap() * 1_000_000_000 + u64::try_from(res.tv_nsec).unwrap()
148        }
149        #[cfg(windows)]
150        {
151            use windows_sys::Win32::System::Performance::QueryPerformanceFrequency;
152
153            unsafe {
154                let mut frequency = 0;
155                if QueryPerformanceFrequency(&mut frequency) == 0 {
156                    panic!(
157                        "QueryPerformanceFrequency failed: {}",
158                        std::io::Error::last_os_error()
159                    );
160                }
161                1_000_000_000 / u64::try_from(frequency).unwrap()
162            }
163        }
164    }
165
166    fn now(&self) -> u64 {
167        // Unwrap here and in `resolution` above; a `u64` is wide enough to
168        // hold over 584 years of nanoseconds.
169        Instant::now()
170            .duration_since(self.initial)
171            .as_nanos()
172            .try_into()
173            .unwrap()
174    }
175}
176
177pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
178    Box::new(MonotonicClock::default())
179}
180
181pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
182    Box::new(WallClock::default())
183}
184
185pub(crate) struct Datetime {
186    pub seconds: i64,
187    pub nanoseconds: u32,
188}
189
190impl TryFrom<SystemTime> for Datetime {
191    type Error = DatetimeError;
192
193    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
194        let epoch = SystemTime::UNIX_EPOCH;
195
196        if time >= epoch {
197            let duration = time.duration_since(epoch)?;
198            Ok(Self {
199                seconds: duration.as_secs().try_into()?,
200                nanoseconds: duration.subsec_nanos(),
201            })
202        } else {
203            let duration = epoch.duration_since(time)?;
204            Ok(Self {
205                seconds: -duration.as_secs().try_into()?,
206                nanoseconds: duration.subsec_nanos(),
207            })
208        }
209    }
210}
211
212#[derive(Debug)]
213pub struct DatetimeError;
214
215impl fmt::Display for DatetimeError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        f.write_str("couldn't represent time as a WASI `Datetime`")
218    }
219}
220
221impl Error for DatetimeError {}
222
223impl From<std::time::SystemTimeError> for DatetimeError {
224    fn from(_: std::time::SystemTimeError) -> Self {
225        DatetimeError
226    }
227}
228
229impl From<std::num::TryFromIntError> for DatetimeError {
230    fn from(_: std::num::TryFromIntError) -> Self {
231        DatetimeError
232    }
233}
234
235/// A helper struct which implements [`HasData`] for the `wasi:clocks` APIs
236/// when used in combination with named imports.
237///
238/// This structure is similar in purpose to [`WasiClocks`] and is used
239/// when using the [`named_imports`] module for `wasi:clocks`. This structure
240/// serves as the `D` type parameter for `add_to_linker` functions.
241///
242/// [`named_imports`]: crate::p3::bindings::named_imports::wasi::clocks
243///
244/// # Meaning of the `T` parameter
245///
246/// Here the `T` must be something that implements [`WasiClocksNamedView`]. The
247/// corresponding `Data` for this type is [`WasiCtxNamedView`] which internally
248/// will contain `&mut T`.
249///
250/// Effectively you're going to implement [`WasiClocksNamedView`] for something in
251/// your embedding, and that's the `T` you'll fill in here.
252///
253/// # Examples
254///
255/// ```
256/// use wasmtime::component::{Linker, Component, ResourceTable};
257/// use wasmtime::{Engine, Result};
258/// use wasmtime_wasi::{NamedId, WasiCtxNamedView};
259/// use wasmtime_wasi::clocks::*;
260/// use wasmtime_wasi::p2::bindings::named_imports;
261/// use std::collections::HashMap;
262///
263/// struct MyStoreState {
264///     table: ResourceTable,
265///     states: HashMap<NamedId, WasiClocksCtx>,
266/// }
267///
268/// fn main() -> Result<()> {
269///     let engine = Engine::default();
270///     let mut linker = Linker::new(&engine);
271///     let component = Component::new(&engine, "(component)")?;
272///     let mut name_map = HashMap::new();
273///
274///     named_imports::wasi::clocks::wall_clock::add_to_linker::<MyStoreState, WasiClocksNamed<MyStoreState>>(
275///         &mut linker,
276///         &component,
277///         |name| {
278///             let len = name_map.len();
279///             Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
280///         },
281///         |state| WasiCtxNamedView(state),
282///     )?;
283///     Ok(())
284/// }
285///
286/// impl WasiClocksNamedView for MyStoreState {
287///     fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_> {
288///         let ctx = self.states.get_mut(&id).expect("state for id");
289///         WasiClocksCtxView {
290///             table: &mut self.table,
291///             ctx,
292///         }
293///     }
294/// }
295/// ```
296pub struct WasiClocksNamed<T>(marker::PhantomData<fn() -> T>);
297
298impl<T> HasData for WasiClocksNamed<T>
299where
300    T: WasiClocksNamedView,
301{
302    type Data<'a> = WasiCtxNamedView<'a, T>;
303}
304
305/// A trait used to look up a specific `wasi:clocks` context for a named
306/// import.
307///
308/// This trait is used in conjunction with the [`named_imports`] bindings
309/// generated for all WASI interfaces. The purpose of this trait is for
310/// embedders to define how a [`NamedId`] maps to a particular `wasi:clocks`
311/// context, here returned as [`WasiClocksCtxView`]. Embedders are responsible
312/// for assigning meaning to [`NamedId`] values themselves. These IDs are
313/// assigned when [`add_named_to_linker`] is called, for example, as the
314/// `lookup` argument to that function.
315///
316/// When using [`add_named_to_linker`] it's sufficient to implement this trait
317/// for the `T` in `Store<T>`. You can also instead implement the
318/// [`WasiNamedView`] trait for `T` which implies an implementation of this
319/// trait.
320///
321/// When using `add_to_linker` in the generated `bindings::named_imports`
322/// module then values implementing this live within the `T` of `Store<T>`, and
323/// be temporarily referenced in [`WasiCtxNamedView`] where internally that'll
324/// hold `WasiCtxNamedView(&mut your_type)`.
325///
326/// [`named_imports`]: crate::p3::bindings::named_imports
327/// [`add_named_to_linker`]: crate::p3::clocks::add_named_to_linker
328/// [`WasiNamedView`]: crate::WasiNamedView
329///
330/// # Examples
331///
332/// ```
333/// use wasmtime::component::{Linker, Component, ResourceTable};
334/// use wasmtime::{Engine, Result};
335/// use wasmtime_wasi::{NamedId, WasiCtxNamedView};
336/// use wasmtime_wasi::clocks::*;
337/// use std::collections::HashMap;
338///
339/// struct MyStoreState {
340///     table: ResourceTable,
341///     states: HashMap<NamedId, WasiClocksCtx>,
342/// }
343///
344/// fn main() -> Result<()> {
345///     let engine = Engine::default();
346///     let mut linker = Linker::new(&engine);
347///     let component = Component::new(&engine, "(component)")?;
348///     let mut name_map = HashMap::new();
349///
350///     wasmtime_wasi::p3::clocks::add_named_to_linker::<MyStoreState>(
351///         &mut linker,
352///         &component,
353///         |_, name| {
354///             let len = name_map.len();
355///             Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
356///         },
357///     )?;
358///     Ok(())
359/// }
360///
361/// impl WasiClocksNamedView for MyStoreState {
362///     fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_> {
363///         let ctx = self.states.get_mut(&id).expect("state for id");
364///         WasiClocksCtxView {
365///             table: &mut self.table,
366///             ctx,
367///         }
368///     }
369/// }
370/// ```
371pub trait WasiClocksNamedView: Send + 'static {
372    /// Looks up the [`WasiClocksCtxView`] for the given [`NamedId`].
373    ///
374    /// This method will resolve the `id` specified to a specific clocks
375    /// context that is available to be used. Note that this method is
376    /// specifically infallible meaning that a clocks context must be returned
377    /// and this cannot generate a trap or panic or similar.
378    ///
379    /// Embedders are responsible for allocating [`NamedId`] and assigning
380    /// meaning to ids. When a `Linker` is populated embedders will have the
381    /// ability to generate a `NamedId` for all imports found, and then that
382    /// embedder-allocated id is then passed back here when the corresponding
383    /// imported function is invoked.
384    ///
385    /// Note that the [`ResourceTable`] referenced in the returned
386    /// [`WasiClocksCtxView`] need not be unique. It's ok to use the same
387    /// [`ResourceTable`] for all imports. This is not a guest-visible
388    /// abstraction and just helps the host allocate and manage state.
389    fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_>;
390}