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 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/// # Examples
17///
18/// ```
19/// use wasmtime::component::{Linker, ResourceTable};
20/// use wasmtime::{Engine, Result, Config};
21/// use wasmtime_wasi::clocks::*;
22///
23/// struct MyStoreState {
24///     table: ResourceTable,
25///     clocks: WasiClocksCtx,
26/// }
27///
28/// fn main() -> Result<()> {
29///     let mut config = Config::new();
30///     config.async_support(true);
31///     let engine = Engine::new(&config)?;
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
83pub struct WallClock {
84    /// The underlying system clock.
85    clock: cap_std::time::SystemClock,
86}
87
88impl Default for WallClock {
89    fn default() -> Self {
90        Self::new(ambient_authority())
91    }
92}
93
94impl WallClock {
95    pub fn new(ambient_authority: AmbientAuthority) -> Self {
96        Self {
97            clock: cap_std::time::SystemClock::new(ambient_authority),
98        }
99    }
100}
101
102impl HostWallClock for WallClock {
103    fn resolution(&self) -> Duration {
104        self.clock.resolution()
105    }
106
107    fn now(&self) -> Duration {
108        // WASI defines wall clocks to return "Unix time".
109        self.clock
110            .now()
111            .duration_since(SystemClock::UNIX_EPOCH)
112            .unwrap()
113    }
114}
115
116pub struct MonotonicClock {
117    /// The underlying system clock.
118    clock: cap_std::time::MonotonicClock,
119
120    /// The `Instant` this clock was created. All returned times are
121    /// durations since that time.
122    initial: Instant,
123}
124
125impl Default for MonotonicClock {
126    fn default() -> Self {
127        Self::new(ambient_authority())
128    }
129}
130
131impl MonotonicClock {
132    pub fn new(ambient_authority: AmbientAuthority) -> Self {
133        let clock = cap_std::time::MonotonicClock::new(ambient_authority);
134        let initial = clock.now();
135        Self { clock, initial }
136    }
137}
138
139impl HostMonotonicClock for MonotonicClock {
140    fn resolution(&self) -> u64 {
141        self.clock.resolution().as_nanos().try_into().unwrap()
142    }
143
144    fn now(&self) -> u64 {
145        // Unwrap here and in `resolution` above; a `u64` is wide enough to
146        // hold over 584 years of nanoseconds.
147        self.clock
148            .now()
149            .duration_since(self.initial)
150            .as_nanos()
151            .try_into()
152            .unwrap()
153    }
154}
155
156pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
157    Box::new(MonotonicClock::default())
158}
159
160pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
161    Box::new(WallClock::default())
162}
163
164pub(crate) struct Datetime {
165    pub seconds: u64,
166    pub nanoseconds: u32,
167}
168
169impl TryFrom<SystemTime> for Datetime {
170    type Error = wasmtime::Error;
171
172    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
173        let duration =
174            time.duration_since(SystemTime::from_std(std::time::SystemTime::UNIX_EPOCH))?;
175
176        Ok(Self {
177            seconds: duration.as_secs(),
178            nanoseconds: duration.subsec_nanos(),
179        })
180    }
181}