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}