1use std::error::Error;
2use std::fmt;
3use std::time::{Duration, Instant, SystemTime};
4use wasmtime::component::{HasData, ResourceTable};
5
6pub 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
83#[derive(Default)]
84pub struct WallClock;
85
86impl WallClock {
87 pub fn new() -> Self {
88 Self
89 }
90}
91
92impl HostWallClock for WallClock {
93 fn resolution(&self) -> Duration {
94 #[cfg(unix)]
95 {
96 let res = rustix::time::clock_getres(rustix::time::ClockId::Realtime);
97 Duration::new(
98 res.tv_sec.try_into().unwrap(),
99 res.tv_nsec.try_into().unwrap(),
100 )
101 }
102 #[cfg(windows)]
103 {
104 Duration::new(0, 55_000_000)
109 }
110 }
111
112 fn now(&self) -> Duration {
113 SystemTime::now()
115 .duration_since(SystemTime::UNIX_EPOCH)
116 .unwrap()
117 }
118}
119
120pub struct MonotonicClock {
121 initial: Instant,
124}
125
126impl Default for MonotonicClock {
127 fn default() -> Self {
128 Self::new()
129 }
130}
131
132impl MonotonicClock {
133 pub fn new() -> Self {
134 Self {
135 initial: Instant::now(),
136 }
137 }
138}
139
140impl HostMonotonicClock for MonotonicClock {
141 fn resolution(&self) -> u64 {
142 #[cfg(unix)]
143 {
144 let res = rustix::time::clock_getres(rustix::time::ClockId::Monotonic);
145 u64::try_from(res.tv_sec).unwrap() * 1_000_000_000 + u64::try_from(res.tv_nsec).unwrap()
146 }
147 #[cfg(windows)]
148 {
149 use windows_sys::Win32::System::Performance::QueryPerformanceFrequency;
150
151 unsafe {
152 let mut frequency = 0;
153 if QueryPerformanceFrequency(&mut frequency) == 0 {
154 panic!(
155 "QueryPerformanceFrequency failed: {}",
156 std::io::Error::last_os_error()
157 );
158 }
159 1_000_000_000 / u64::try_from(frequency).unwrap()
160 }
161 }
162 }
163
164 fn now(&self) -> u64 {
165 Instant::now()
168 .duration_since(self.initial)
169 .as_nanos()
170 .try_into()
171 .unwrap()
172 }
173}
174
175pub fn monotonic_clock() -> Box<dyn HostMonotonicClock + Send> {
176 Box::new(MonotonicClock::default())
177}
178
179pub fn wall_clock() -> Box<dyn HostWallClock + Send> {
180 Box::new(WallClock::default())
181}
182
183pub(crate) struct Datetime {
184 pub seconds: i64,
185 pub nanoseconds: u32,
186}
187
188impl TryFrom<SystemTime> for Datetime {
189 type Error = DatetimeError;
190
191 fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
192 let epoch = SystemTime::UNIX_EPOCH;
193
194 if time >= epoch {
195 let duration = time.duration_since(epoch)?;
196 Ok(Self {
197 seconds: duration.as_secs().try_into()?,
198 nanoseconds: duration.subsec_nanos(),
199 })
200 } else {
201 let duration = epoch.duration_since(time)?;
202 Ok(Self {
203 seconds: -duration.as_secs().try_into()?,
204 nanoseconds: duration.subsec_nanos(),
205 })
206 }
207 }
208}
209
210#[derive(Debug)]
211pub struct DatetimeError;
212
213impl fmt::Display for DatetimeError {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 f.write_str("couldn't represent time as a WASI `Datetime`")
216 }
217}
218
219impl Error for DatetimeError {}
220
221impl From<std::time::SystemTimeError> for DatetimeError {
222 fn from(_: std::time::SystemTimeError) -> Self {
223 DatetimeError
224 }
225}
226
227impl From<std::num::TryFromIntError> for DatetimeError {
228 fn from(_: std::num::TryFromIntError) -> Self {
229 DatetimeError
230 }
231}