Skip to main content

wasmtime_wasi/
clocks.rs

1use cap_std::time::{Duration, Instant, SystemClock, SystemTime};
2use cap_std::{AmbientAuthority, ambient_authority};
3use cap_time_ext::{MonotonicClockExt as _, SystemClockExt as _};
4use std::error::Error;
5use std::fmt;
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
85pub struct WallClock {
86    /// The underlying system clock.
87    clock: cap_std::time::SystemClock,
88}
89
90impl Default for WallClock {
91    fn default() -> Self {
92        Self::new(ambient_authority())
93    }
94}
95
96impl WallClock {
97    pub fn new(ambient_authority: AmbientAuthority) -> Self {
98        Self {
99            clock: cap_std::time::SystemClock::new(ambient_authority),
100        }
101    }
102}
103
104impl HostWallClock for WallClock {
105    fn resolution(&self) -> Duration {
106        self.clock.resolution()
107    }
108
109    fn now(&self) -> Duration {
110        // WASI defines wall clocks to return "Unix time".
111        self.clock
112            .now()
113            .duration_since(SystemClock::UNIX_EPOCH)
114            .unwrap()
115    }
116}
117
118pub struct MonotonicClock {
119    /// The underlying system clock.
120    clock: cap_std::time::MonotonicClock,
121
122    /// The `Instant` this clock was created. All returned times are
123    /// durations since that time.
124    initial: Instant,
125}
126
127impl Default for MonotonicClock {
128    fn default() -> Self {
129        Self::new(ambient_authority())
130    }
131}
132
133impl MonotonicClock {
134    pub fn new(ambient_authority: AmbientAuthority) -> Self {
135        let clock = cap_std::time::MonotonicClock::new(ambient_authority);
136        let initial = clock.now();
137        Self { clock, initial }
138    }
139}
140
141impl HostMonotonicClock for MonotonicClock {
142    fn resolution(&self) -> u64 {
143        self.clock.resolution().as_nanos().try_into().unwrap()
144    }
145
146    fn now(&self) -> u64 {
147        // Unwrap here and in `resolution` above; a `u64` is wide enough to
148        // hold over 584 years of nanoseconds.
149        self.clock
150            .now()
151            .duration_since(self.initial)
152            .as_nanos()
153            .try_into()
154            .unwrap()
155    }
156}
157
158pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
159    Box::new(MonotonicClock::default())
160}
161
162pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
163    Box::new(WallClock::default())
164}
165
166pub(crate) struct Datetime {
167    pub seconds: i64,
168    pub nanoseconds: u32,
169}
170
171impl TryFrom<SystemTime> for Datetime {
172    type Error = DatetimeError;
173
174    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
175        let epoch = SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH);
176
177        if time >= epoch {
178            let duration = time.duration_since(epoch)?;
179            Ok(Self {
180                seconds: duration.as_secs().try_into()?,
181                nanoseconds: duration.subsec_nanos(),
182            })
183        } else {
184            let duration = epoch.duration_since(time)?;
185            Ok(Self {
186                seconds: -duration.as_secs().try_into()?,
187                nanoseconds: duration.subsec_nanos(),
188            })
189        }
190    }
191}
192
193#[derive(Debug)]
194pub struct DatetimeError;
195
196impl fmt::Display for DatetimeError {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.write_str("couldn't represent time as a WASI `Datetime`")
199    }
200}
201
202impl Error for DatetimeError {}
203
204impl From<std::time::SystemTimeError> for DatetimeError {
205    fn from(_: std::time::SystemTimeError) -> Self {
206        DatetimeError
207    }
208}
209
210impl From<std::num::TryFromIntError> for DatetimeError {
211    fn from(_: std::num::TryFromIntError) -> Self {
212        DatetimeError
213    }
214}