Skip to main content

wasmtime_wasi/
clocks.rs

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