xref: /wasmtime-44.0.1/crates/wasi/src/clocks.rs (revision bc4582c3)
1 use cap_std::time::{Duration, Instant, SystemClock, SystemTime};
2 use cap_std::{AmbientAuthority, ambient_authority};
3 use cap_time_ext::{MonotonicClockExt as _, SystemClockExt as _};
4 use std::error::Error;
5 use std::fmt;
6 use 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 /// ```
46 pub struct WasiClocks;
47 
48 impl HasData for WasiClocks {
49     type Data<'a> = WasiClocksCtxView<'a>;
50 }
51 
52 pub struct WasiClocksCtx {
53     pub(crate) wall_clock: Box<dyn HostWallClock + Send>,
54     pub(crate) monotonic_clock: Box<dyn HostMonotonicClock + Send>,
55 }
56 
57 impl Default for WasiClocksCtx {
default() -> Self58     fn default() -> Self {
59         Self {
60             wall_clock: wall_clock(),
61             monotonic_clock: monotonic_clock(),
62         }
63     }
64 }
65 
66 pub trait WasiClocksView: Send {
clocks(&mut self) -> WasiClocksCtxView<'_>67     fn clocks(&mut self) -> WasiClocksCtxView<'_>;
68 }
69 
70 pub struct WasiClocksCtxView<'a> {
71     pub ctx: &'a mut WasiClocksCtx,
72     pub table: &'a mut ResourceTable,
73 }
74 
75 pub trait HostWallClock: Send {
resolution(&self) -> Duration76     fn resolution(&self) -> Duration;
now(&self) -> Duration77     fn now(&self) -> Duration;
78 }
79 
80 pub trait HostMonotonicClock: Send {
resolution(&self) -> u6481     fn resolution(&self) -> u64;
now(&self) -> u6482     fn now(&self) -> u64;
83 }
84 
85 pub struct WallClock {
86     /// The underlying system clock.
87     clock: cap_std::time::SystemClock,
88 }
89 
90 impl Default for WallClock {
default() -> Self91     fn default() -> Self {
92         Self::new(ambient_authority())
93     }
94 }
95 
96 impl WallClock {
new(ambient_authority: AmbientAuthority) -> Self97     pub fn new(ambient_authority: AmbientAuthority) -> Self {
98         Self {
99             clock: cap_std::time::SystemClock::new(ambient_authority),
100         }
101     }
102 }
103 
104 impl HostWallClock for WallClock {
resolution(&self) -> Duration105     fn resolution(&self) -> Duration {
106         self.clock.resolution()
107     }
108 
now(&self) -> Duration109     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 
118 pub 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 
127 impl Default for MonotonicClock {
default() -> Self128     fn default() -> Self {
129         Self::new(ambient_authority())
130     }
131 }
132 
133 impl MonotonicClock {
new(ambient_authority: AmbientAuthority) -> Self134     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 
141 impl HostMonotonicClock for MonotonicClock {
resolution(&self) -> u64142     fn resolution(&self) -> u64 {
143         self.clock.resolution().as_nanos().try_into().unwrap()
144     }
145 
now(&self) -> u64146     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 
monotonic_clock() -> Box<dyn HostMonotonicClock + Send>158 pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
159     Box::new(MonotonicClock::default())
160 }
161 
wall_clock() -> Box<dyn HostWallClock + Send>162 pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
163     Box::new(WallClock::default())
164 }
165 
166 pub(crate) struct Datetime {
167     pub seconds: i64,
168     pub nanoseconds: u32,
169 }
170 
171 impl TryFrom<SystemTime> for Datetime {
172     type Error = DatetimeError;
173 
try_from(time: SystemTime) -> Result<Self, Self::Error>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)]
194 pub struct DatetimeError;
195 
196 impl fmt::Display for DatetimeError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result197     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198         f.write_str("couldn't represent time as a WASI `Datetime`")
199     }
200 }
201 
202 impl Error for DatetimeError {}
203 
204 impl From<std::time::SystemTimeError> for DatetimeError {
from(_: std::time::SystemTimeError) -> Self205     fn from(_: std::time::SystemTimeError) -> Self {
206         DatetimeError
207     }
208 }
209 
210 impl From<std::num::TryFromIntError> for DatetimeError {
from(_: std::num::TryFromIntError) -> Self211     fn from(_: std::num::TryFromIntError) -> Self {
212         DatetimeError
213     }
214 }
215