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    // When seconds are negative, nanos still represent a forward advancement in
188    // time. E.g. -1s and 999_999_999 nanos is -1ns.
189    pub nanoseconds: u32,
190}
191
192impl TryFrom<SystemTime> for Datetime {
193    type Error = DatetimeError;
194
195    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
196        match time.duration_since(SystemTime::UNIX_EPOCH) {
197            Ok(duration) => Ok(Self {
198                seconds: duration.as_secs().try_into()?,
199                nanoseconds: duration.subsec_nanos(),
200            }),
201            Err(e) => {
202                // If time is before the epoch, the error gives us the duration
203                // from `time` to the epoch.
204                let duration = e.duration();
205                // So both seconds and nanoseconds are negative. We need to
206                // convert it to negative seconds and positive nanos which
207                // matches the WASI spec.
208                let nanoseconds = duration.subsec_nanos();
209                if nanoseconds == 0 {
210                    Ok(Self {
211                        // Negation cannot panic because duration.as_secs returns a
212                        // u64.
213                        seconds: (-i128::from(duration.as_secs())).try_into()?,
214                        nanoseconds: 0,
215                    })
216                } else {
217                    Ok(Self {
218                        // Negation and (- 1) cannot panic because duration.as_secs
219                        // returns a u64.
220                        seconds: (-i128::from(duration.as_secs()) - 1).try_into()?,
221                        // Cannot panic because `subsec_nanos` guarantees the result
222                        // is less than 1_000_000_000.
223                        nanoseconds: 1_000_000_000 - nanoseconds,
224                    })
225                }
226            }
227        }
228    }
229}
230
231#[derive(Debug)]
232pub struct DatetimeError;
233
234impl fmt::Display for DatetimeError {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        f.write_str("couldn't represent time as a WASI `Datetime`")
237    }
238}
239
240impl Error for DatetimeError {}
241
242impl From<std::time::SystemTimeError> for DatetimeError {
243    fn from(_: std::time::SystemTimeError) -> Self {
244        DatetimeError
245    }
246}
247
248impl From<std::num::TryFromIntError> for DatetimeError {
249    fn from(_: std::num::TryFromIntError) -> Self {
250        DatetimeError
251    }
252}
253
254/// A helper struct which implements [`HasData`] for the `wasi:clocks` APIs
255/// when used in combination with named imports.
256///
257/// This structure is similar in purpose to [`WasiClocks`] and is used
258/// when using the [`named_imports`] module for `wasi:clocks`. This structure
259/// serves as the `D` type parameter for `add_to_linker` functions.
260///
261/// [`named_imports`]: crate::p3::bindings::named_imports::wasi::clocks
262///
263/// # Meaning of the `T` parameter
264///
265/// Here the `T` must be something that implements [`WasiClocksNamedView`]. The
266/// corresponding `Data` for this type is [`WasiCtxNamedView`] which internally
267/// will contain `&mut T`.
268///
269/// Effectively you're going to implement [`WasiClocksNamedView`] for something in
270/// your embedding, and that's the `T` you'll fill in here.
271///
272/// # Examples
273///
274/// ```
275/// use wasmtime::component::{Linker, Component, ResourceTable};
276/// use wasmtime::{Engine, Result};
277/// use wasmtime_wasi::{NamedId, WasiCtxNamedView};
278/// use wasmtime_wasi::clocks::*;
279/// use wasmtime_wasi::p2::bindings::named_imports;
280/// use std::collections::HashMap;
281///
282/// struct MyStoreState {
283///     table: ResourceTable,
284///     states: HashMap<NamedId, WasiClocksCtx>,
285/// }
286///
287/// fn main() -> Result<()> {
288///     let engine = Engine::default();
289///     let mut linker = Linker::new(&engine);
290///     let component = Component::new(&engine, "(component)")?;
291///     let mut name_map = HashMap::new();
292///
293///     named_imports::wasi::clocks::wall_clock::add_to_linker::<MyStoreState, WasiClocksNamed<MyStoreState>>(
294///         &mut linker,
295///         &component,
296///         |name| {
297///             let len = name_map.len();
298///             Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
299///         },
300///         |state| WasiCtxNamedView(state),
301///     )?;
302///     Ok(())
303/// }
304///
305/// impl WasiClocksNamedView for MyStoreState {
306///     fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_> {
307///         let ctx = self.states.get_mut(&id).expect("state for id");
308///         WasiClocksCtxView {
309///             table: &mut self.table,
310///             ctx,
311///         }
312///     }
313/// }
314/// ```
315pub struct WasiClocksNamed<T>(marker::PhantomData<fn() -> T>);
316
317impl<T> HasData for WasiClocksNamed<T>
318where
319    T: WasiClocksNamedView,
320{
321    type Data<'a> = WasiCtxNamedView<'a, T>;
322}
323
324/// A trait used to look up a specific `wasi:clocks` context for a named
325/// import.
326///
327/// This trait is used in conjunction with the [`named_imports`] bindings
328/// generated for all WASI interfaces. The purpose of this trait is for
329/// embedders to define how a [`NamedId`] maps to a particular `wasi:clocks`
330/// context, here returned as [`WasiClocksCtxView`]. Embedders are responsible
331/// for assigning meaning to [`NamedId`] values themselves. These IDs are
332/// assigned when [`add_named_to_linker`] is called, for example, as the
333/// `lookup` argument to that function.
334///
335/// When using [`add_named_to_linker`] it's sufficient to implement this trait
336/// for the `T` in `Store<T>`. You can also instead implement the
337/// [`WasiNamedView`] trait for `T` which implies an implementation of this
338/// trait.
339///
340/// When using `add_to_linker` in the generated `bindings::named_imports`
341/// module then values implementing this live within the `T` of `Store<T>`, and
342/// be temporarily referenced in [`WasiCtxNamedView`] where internally that'll
343/// hold `WasiCtxNamedView(&mut your_type)`.
344///
345/// [`named_imports`]: crate::p3::bindings::named_imports
346/// [`add_named_to_linker`]: crate::p3::clocks::add_named_to_linker
347/// [`WasiNamedView`]: crate::WasiNamedView
348///
349/// # Examples
350///
351/// ```
352/// use wasmtime::component::{Linker, Component, ResourceTable};
353/// use wasmtime::{Engine, Result};
354/// use wasmtime_wasi::{NamedId, WasiCtxNamedView};
355/// use wasmtime_wasi::clocks::*;
356/// use std::collections::HashMap;
357///
358/// struct MyStoreState {
359///     table: ResourceTable,
360///     states: HashMap<NamedId, WasiClocksCtx>,
361/// }
362///
363/// fn main() -> Result<()> {
364///     let engine = Engine::default();
365///     let mut linker = Linker::new(&engine);
366///     let component = Component::new(&engine, "(component)")?;
367///     let mut name_map = HashMap::new();
368///
369///     wasmtime_wasi::p3::clocks::add_named_to_linker::<MyStoreState>(
370///         &mut linker,
371///         &component,
372///         |_, name| {
373///             let len = name_map.len();
374///             Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
375///         },
376///     )?;
377///     Ok(())
378/// }
379///
380/// impl WasiClocksNamedView for MyStoreState {
381///     fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_> {
382///         let ctx = self.states.get_mut(&id).expect("state for id");
383///         WasiClocksCtxView {
384///             table: &mut self.table,
385///             ctx,
386///         }
387///     }
388/// }
389/// ```
390pub trait WasiClocksNamedView: Send + 'static {
391    /// Looks up the [`WasiClocksCtxView`] for the given [`NamedId`].
392    ///
393    /// This method will resolve the `id` specified to a specific clocks
394    /// context that is available to be used. Note that this method is
395    /// specifically infallible meaning that a clocks context must be returned
396    /// and this cannot generate a trap or panic or similar.
397    ///
398    /// Embedders are responsible for allocating [`NamedId`] and assigning
399    /// meaning to ids. When a `Linker` is populated embedders will have the
400    /// ability to generate a `NamedId` for all imports found, and then that
401    /// embedder-allocated id is then passed back here when the corresponding
402    /// imported function is invoked.
403    ///
404    /// It's strongly recommended, though not always required, that the
405    /// [`ResourceTable`] in the returned [`WasiClocksCtxView`] is the same
406    /// table that the default context, [`WasiClocksView::clocks`], uses.
407    /// Resources created through a named import can be used through interfaces
408    /// bound to the default context (and vice versa), and with separate tables
409    /// those lookups fail or find another context's resources.
410    fn clocks(&mut self, id: NamedId) -> WasiClocksCtxView<'_>;
411}