diff --git a/crates/wasi-common/cap-std-sync/src/clocks.rs b/crates/wasi-common/cap-std-sync/src/clocks.rs index 19e357435a..71eb34252b 100644 --- a/crates/wasi-common/cap-std-sync/src/clocks.rs +++ b/crates/wasi-common/cap-std-sync/src/clocks.rs @@ -35,13 +35,7 @@ impl WasiMonotonicClock for MonotonicClock { } pub fn clocks_ctx() -> WasiClocks { - let system = Box::new(SystemClock::new(ambient_authority())); - let monotonic = cap_std::time::MonotonicClock::new(ambient_authority()); - let creation_time = monotonic.now(); - let monotonic = Box::new(MonotonicClock(monotonic)); - WasiClocks { - system, - monotonic, - creation_time, - } + WasiClocks::new() + .with_system(SystemClock::new(ambient_authority())) + .with_monotonic(MonotonicClock::new(ambient_authority())) } diff --git a/crates/wasi-common/src/clocks.rs b/crates/wasi-common/src/clocks.rs index 679759caf9..d72f2f0d99 100644 --- a/crates/wasi-common/src/clocks.rs +++ b/crates/wasi-common/src/clocks.rs @@ -1,3 +1,4 @@ +use crate::{Error, ErrorExt}; use cap_std::time::{Duration, Instant, SystemTime}; pub enum SystemTimeSpec { @@ -15,8 +16,52 @@ pub trait WasiMonotonicClock: Send + Sync { fn now(&self, precision: Duration) -> Instant; } -pub struct WasiClocks { - pub system: Box, - pub monotonic: Box, +pub struct WasiMonotonicOffsetClock { pub creation_time: cap_std::time::Instant, + pub abs_clock: Box, +} + +impl WasiMonotonicOffsetClock { + pub fn new(clock: impl 'static + WasiMonotonicClock) -> Self { + Self { + creation_time: clock.now(clock.resolution()), + abs_clock: Box::new(clock), + } + } +} + +pub struct WasiClocks { + pub system: Option>, + pub monotonic: Option, +} + +impl WasiClocks { + pub fn new() -> Self { + Self { + system: None, + monotonic: None, + } + } + + pub fn with_system(mut self, clock: impl 'static + WasiSystemClock) -> Self { + self.system = Some(Box::new(clock)); + self + } + + pub fn with_monotonic(mut self, clock: impl 'static + WasiMonotonicClock) -> Self { + self.monotonic = Some(WasiMonotonicOffsetClock::new(clock)); + self + } + + pub fn system(&self) -> Result<&dyn WasiSystemClock, Error> { + self.system + .as_deref() + .ok_or_else(|| Error::badf().context("system clock is not supported")) + } + + pub fn monotonic(&self) -> Result<&WasiMonotonicOffsetClock, Error> { + self.monotonic + .as_ref() + .ok_or_else(|| Error::badf().context("monotonic clock is not supported")) + } } diff --git a/crates/wasi-common/src/snapshots/preview_0.rs b/crates/wasi-common/src/snapshots/preview_0.rs index e2a47223af..55dc08bd13 100644 --- a/crates/wasi-common/src/snapshots/preview_0.rs +++ b/crates/wasi-common/src/snapshots/preview_0.rs @@ -959,25 +959,22 @@ impl wasi_unstable::WasiUnstable for WasiCtx { match sub.u { types::SubscriptionU::Clock(clocksub) => match clocksub.id { types::Clockid::Monotonic => { - let clock = self.clocks.monotonic.deref(); + let clock = self.clocks.monotonic()?; let precision = Duration::from_nanos(clocksub.precision); let duration = Duration::from_nanos(clocksub.timeout); - let deadline = if clocksub + let start = if clocksub .flags .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME) { - self.clocks - .creation_time - .checked_add(duration) - .ok_or_else(|| Error::overflow().context("deadline"))? + clock.creation_time } else { - clock - .now(precision) - .checked_add(duration) - .ok_or_else(|| Error::overflow().context("deadline"))? + clock.abs_clock.now(precision) }; + let deadline = start + .checked_add(duration) + .ok_or_else(|| Error::overflow().context("deadline"))?; poll.subscribe_monotonic_clock( - clock, + &*clock.abs_clock, deadline, precision, sub.userdata.into(), diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index 8b924c9dcc..d7b0d16f7a 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -68,8 +68,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { async fn clock_res_get(&mut self, id: types::Clockid) -> Result { let resolution = match id { - types::Clockid::Realtime => Ok(self.clocks.system.resolution()), - types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()), + types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()), + types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()), types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => { Err(Error::badf().context("process and thread clocks are not supported")) } @@ -85,7 +85,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { let precision = Duration::from_nanos(precision); match id { types::Clockid::Realtime => { - let now = self.clocks.system.now(precision).into_std(); + let now = self.clocks.system()?.now(precision).into_std(); let d = now .duration_since(std::time::SystemTime::UNIX_EPOCH) .map_err(|_| { @@ -94,8 +94,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(d.as_nanos().try_into()?) } types::Clockid::Monotonic => { - let now = self.clocks.monotonic.now(precision); - let d = now.duration_since(self.clocks.creation_time); + let clock = self.clocks.monotonic()?; + let now = clock.abs_clock.now(precision); + let d = now.duration_since(clock.creation_time); Ok(d.as_nanos().try_into()?) } types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => { @@ -909,25 +910,22 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { match sub.u { types::SubscriptionU::Clock(clocksub) => match clocksub.id { types::Clockid::Monotonic => { - let clock = self.clocks.monotonic.deref(); + let clock = self.clocks.monotonic()?; let precision = Duration::from_nanos(clocksub.precision); let duration = Duration::from_nanos(clocksub.timeout); - let deadline = if clocksub + let start = if clocksub .flags .contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME) { - self.clocks - .creation_time - .checked_add(duration) - .ok_or_else(|| Error::overflow().context("deadline"))? + clock.creation_time } else { - clock - .now(precision) - .checked_add(duration) - .ok_or_else(|| Error::overflow().context("deadline"))? + clock.abs_clock.now(precision) }; + let deadline = start + .checked_add(duration) + .ok_or_else(|| Error::overflow().context("deadline"))?; poll.subscribe_monotonic_clock( - clock, + &*clock.abs_clock, deadline, precision, sub.userdata.into(), @@ -939,7 +937,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { // on threads waiting in these functions. MONOTONIC should always have // resolution at least as good as REALTIME, so we can translate a // non-absolute `REALTIME` request into a `MONOTONIC` request. - let clock = self.clocks.monotonic.deref(); + let clock = self.clocks.monotonic()?; let precision = Duration::from_nanos(clocksub.precision); let duration = Duration::from_nanos(clocksub.timeout); let deadline = if clocksub @@ -949,12 +947,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { return Err(Error::not_supported()); } else { clock + .abs_clock .now(precision) .checked_add(duration) .ok_or_else(|| Error::overflow().context("deadline"))? }; poll.subscribe_monotonic_clock( - clock, + &*clock.abs_clock, deadline, precision, sub.userdata.into(), diff --git a/crates/wasi-common/tokio/tests/poll_oneoff.rs b/crates/wasi-common/tokio/tests/poll_oneoff.rs index 9ba85f6dee..f305b7c44d 100644 --- a/crates/wasi-common/tokio/tests/poll_oneoff.rs +++ b/crates/wasi-common/tokio/tests/poll_oneoff.rs @@ -38,14 +38,14 @@ async fn empty_file_readable() -> Result<(), Error> { let mut poll = Poll::new(); poll.subscribe_read(&mut *f, Userdata::from(123)); // Timeout bounds time in poll_oneoff + let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( - &*clocks.monotonic, - clocks - .monotonic - .now(clocks.monotonic.resolution()) + monotonic, + monotonic + .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(), - clocks.monotonic.resolution(), + monotonic.resolution(), Userdata::from(0), ); poll_oneoff(&mut poll).await?; @@ -81,14 +81,14 @@ async fn empty_file_writable() -> Result<(), Error> { let mut poll = Poll::new(); poll.subscribe_write(&mut *writable_f, Userdata::from(123)); // Timeout bounds time in poll_oneoff + let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( - &*clocks.monotonic, - clocks - .monotonic - .now(clocks.monotonic.resolution()) + monotonic, + monotonic + .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(), - clocks.monotonic.resolution(), + monotonic.resolution(), Userdata::from(0), ); poll_oneoff(&mut poll).await?; @@ -110,9 +110,9 @@ async fn empty_file_writable() -> Result<(), Error> { async fn stdio_readable() -> Result<(), Error> { let clocks = clocks_ctx(); - let deadline = clocks - .monotonic - .now(clocks.monotonic.resolution()) + let monotonic = &*clocks.monotonic()?.abs_clock; + let deadline = monotonic + .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(); @@ -133,10 +133,11 @@ async fn stdio_readable() -> Result<(), Error> { poll.subscribe_write(&mut **file, Userdata::from(*ix)); } // Timeout bounds time in poll_oneoff + let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( - &*clocks.monotonic, + monotonic, deadline, - clocks.monotonic.resolution(), + monotonic.resolution(), Userdata::from(999), ); poll_oneoff(&mut poll).await?;