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