Allow to disable clocks in WasiCtx (#6007)
Takes the approach described in #6004, but also creates a wrapper for the monotonic time that encapsulates the `creation_time` field as well, since they logically belong and are always used together. This makes it easier to configure `WasiCtx` with custom clocks as well as disable them for security or determinism reasons. Closes #6004.
This commit is contained in:
@@ -35,13 +35,7 @@ impl WasiMonotonicClock for MonotonicClock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn clocks_ctx() -> WasiClocks {
|
pub fn clocks_ctx() -> WasiClocks {
|
||||||
let system = Box::new(SystemClock::new(ambient_authority()));
|
WasiClocks::new()
|
||||||
let monotonic = cap_std::time::MonotonicClock::new(ambient_authority());
|
.with_system(SystemClock::new(ambient_authority()))
|
||||||
let creation_time = monotonic.now();
|
.with_monotonic(MonotonicClock::new(ambient_authority()))
|
||||||
let monotonic = Box::new(MonotonicClock(monotonic));
|
|
||||||
WasiClocks {
|
|
||||||
system,
|
|
||||||
monotonic,
|
|
||||||
creation_time,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::{Error, ErrorExt};
|
||||||
use cap_std::time::{Duration, Instant, SystemTime};
|
use cap_std::time::{Duration, Instant, SystemTime};
|
||||||
|
|
||||||
pub enum SystemTimeSpec {
|
pub enum SystemTimeSpec {
|
||||||
@@ -15,8 +16,52 @@ pub trait WasiMonotonicClock: Send + Sync {
|
|||||||
fn now(&self, precision: Duration) -> Instant;
|
fn now(&self, precision: Duration) -> Instant;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WasiClocks {
|
pub struct WasiMonotonicOffsetClock {
|
||||||
pub system: Box<dyn WasiSystemClock>,
|
|
||||||
pub monotonic: Box<dyn WasiMonotonicClock>,
|
|
||||||
pub creation_time: cap_std::time::Instant,
|
pub creation_time: cap_std::time::Instant,
|
||||||
|
pub abs_clock: Box<dyn WasiMonotonicClock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Box<dyn WasiSystemClock>>,
|
||||||
|
pub monotonic: Option<WasiMonotonicOffsetClock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -959,25 +959,22 @@ impl wasi_unstable::WasiUnstable for WasiCtx {
|
|||||||
match sub.u {
|
match sub.u {
|
||||||
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
|
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
|
||||||
types::Clockid::Monotonic => {
|
types::Clockid::Monotonic => {
|
||||||
let clock = self.clocks.monotonic.deref();
|
let clock = self.clocks.monotonic()?;
|
||||||
let precision = Duration::from_nanos(clocksub.precision);
|
let precision = Duration::from_nanos(clocksub.precision);
|
||||||
let duration = Duration::from_nanos(clocksub.timeout);
|
let duration = Duration::from_nanos(clocksub.timeout);
|
||||||
let deadline = if clocksub
|
let start = if clocksub
|
||||||
.flags
|
.flags
|
||||||
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
||||||
{
|
{
|
||||||
self.clocks
|
clock.creation_time
|
||||||
.creation_time
|
|
||||||
.checked_add(duration)
|
|
||||||
.ok_or_else(|| Error::overflow().context("deadline"))?
|
|
||||||
} else {
|
} else {
|
||||||
clock
|
clock.abs_clock.now(precision)
|
||||||
.now(precision)
|
|
||||||
.checked_add(duration)
|
|
||||||
.ok_or_else(|| Error::overflow().context("deadline"))?
|
|
||||||
};
|
};
|
||||||
|
let deadline = start
|
||||||
|
.checked_add(duration)
|
||||||
|
.ok_or_else(|| Error::overflow().context("deadline"))?;
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
clock,
|
&*clock.abs_clock,
|
||||||
deadline,
|
deadline,
|
||||||
precision,
|
precision,
|
||||||
sub.userdata.into(),
|
sub.userdata.into(),
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
|
|
||||||
async fn clock_res_get(&mut self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
async fn clock_res_get(&mut self, id: types::Clockid) -> Result<types::Timestamp, Error> {
|
||||||
let resolution = match id {
|
let resolution = match id {
|
||||||
types::Clockid::Realtime => Ok(self.clocks.system.resolution()),
|
types::Clockid::Realtime => Ok(self.clocks.system()?.resolution()),
|
||||||
types::Clockid::Monotonic => Ok(self.clocks.monotonic.resolution()),
|
types::Clockid::Monotonic => Ok(self.clocks.monotonic()?.abs_clock.resolution()),
|
||||||
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
|
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
|
||||||
Err(Error::badf().context("process and thread clocks are not supported"))
|
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);
|
let precision = Duration::from_nanos(precision);
|
||||||
match id {
|
match id {
|
||||||
types::Clockid::Realtime => {
|
types::Clockid::Realtime => {
|
||||||
let now = self.clocks.system.now(precision).into_std();
|
let now = self.clocks.system()?.now(precision).into_std();
|
||||||
let d = now
|
let d = now
|
||||||
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@@ -94,8 +94,9 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
Ok(d.as_nanos().try_into()?)
|
Ok(d.as_nanos().try_into()?)
|
||||||
}
|
}
|
||||||
types::Clockid::Monotonic => {
|
types::Clockid::Monotonic => {
|
||||||
let now = self.clocks.monotonic.now(precision);
|
let clock = self.clocks.monotonic()?;
|
||||||
let d = now.duration_since(self.clocks.creation_time);
|
let now = clock.abs_clock.now(precision);
|
||||||
|
let d = now.duration_since(clock.creation_time);
|
||||||
Ok(d.as_nanos().try_into()?)
|
Ok(d.as_nanos().try_into()?)
|
||||||
}
|
}
|
||||||
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
|
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
|
||||||
@@ -909,25 +910,22 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
match sub.u {
|
match sub.u {
|
||||||
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
|
types::SubscriptionU::Clock(clocksub) => match clocksub.id {
|
||||||
types::Clockid::Monotonic => {
|
types::Clockid::Monotonic => {
|
||||||
let clock = self.clocks.monotonic.deref();
|
let clock = self.clocks.monotonic()?;
|
||||||
let precision = Duration::from_nanos(clocksub.precision);
|
let precision = Duration::from_nanos(clocksub.precision);
|
||||||
let duration = Duration::from_nanos(clocksub.timeout);
|
let duration = Duration::from_nanos(clocksub.timeout);
|
||||||
let deadline = if clocksub
|
let start = if clocksub
|
||||||
.flags
|
.flags
|
||||||
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
.contains(types::Subclockflags::SUBSCRIPTION_CLOCK_ABSTIME)
|
||||||
{
|
{
|
||||||
self.clocks
|
clock.creation_time
|
||||||
.creation_time
|
|
||||||
.checked_add(duration)
|
|
||||||
.ok_or_else(|| Error::overflow().context("deadline"))?
|
|
||||||
} else {
|
} else {
|
||||||
clock
|
clock.abs_clock.now(precision)
|
||||||
.now(precision)
|
|
||||||
.checked_add(duration)
|
|
||||||
.ok_or_else(|| Error::overflow().context("deadline"))?
|
|
||||||
};
|
};
|
||||||
|
let deadline = start
|
||||||
|
.checked_add(duration)
|
||||||
|
.ok_or_else(|| Error::overflow().context("deadline"))?;
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
clock,
|
&*clock.abs_clock,
|
||||||
deadline,
|
deadline,
|
||||||
precision,
|
precision,
|
||||||
sub.userdata.into(),
|
sub.userdata.into(),
|
||||||
@@ -939,7 +937,7 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
// on threads waiting in these functions. MONOTONIC should always have
|
// on threads waiting in these functions. MONOTONIC should always have
|
||||||
// resolution at least as good as REALTIME, so we can translate a
|
// resolution at least as good as REALTIME, so we can translate a
|
||||||
// non-absolute `REALTIME` request into a `MONOTONIC` request.
|
// 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 precision = Duration::from_nanos(clocksub.precision);
|
||||||
let duration = Duration::from_nanos(clocksub.timeout);
|
let duration = Duration::from_nanos(clocksub.timeout);
|
||||||
let deadline = if clocksub
|
let deadline = if clocksub
|
||||||
@@ -949,12 +947,13 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx {
|
|||||||
return Err(Error::not_supported());
|
return Err(Error::not_supported());
|
||||||
} else {
|
} else {
|
||||||
clock
|
clock
|
||||||
|
.abs_clock
|
||||||
.now(precision)
|
.now(precision)
|
||||||
.checked_add(duration)
|
.checked_add(duration)
|
||||||
.ok_or_else(|| Error::overflow().context("deadline"))?
|
.ok_or_else(|| Error::overflow().context("deadline"))?
|
||||||
};
|
};
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
clock,
|
&*clock.abs_clock,
|
||||||
deadline,
|
deadline,
|
||||||
precision,
|
precision,
|
||||||
sub.userdata.into(),
|
sub.userdata.into(),
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ async fn empty_file_readable() -> Result<(), Error> {
|
|||||||
let mut poll = Poll::new();
|
let mut poll = Poll::new();
|
||||||
poll.subscribe_read(&mut *f, Userdata::from(123));
|
poll.subscribe_read(&mut *f, Userdata::from(123));
|
||||||
// Timeout bounds time in poll_oneoff
|
// Timeout bounds time in poll_oneoff
|
||||||
|
let monotonic = &*clocks.monotonic()?.abs_clock;
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
&*clocks.monotonic,
|
monotonic,
|
||||||
clocks
|
monotonic
|
||||||
.monotonic
|
.now(monotonic.resolution())
|
||||||
.now(clocks.monotonic.resolution())
|
|
||||||
.checked_add(TIMEOUT)
|
.checked_add(TIMEOUT)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
clocks.monotonic.resolution(),
|
monotonic.resolution(),
|
||||||
Userdata::from(0),
|
Userdata::from(0),
|
||||||
);
|
);
|
||||||
poll_oneoff(&mut poll).await?;
|
poll_oneoff(&mut poll).await?;
|
||||||
@@ -81,14 +81,14 @@ async fn empty_file_writable() -> Result<(), Error> {
|
|||||||
let mut poll = Poll::new();
|
let mut poll = Poll::new();
|
||||||
poll.subscribe_write(&mut *writable_f, Userdata::from(123));
|
poll.subscribe_write(&mut *writable_f, Userdata::from(123));
|
||||||
// Timeout bounds time in poll_oneoff
|
// Timeout bounds time in poll_oneoff
|
||||||
|
let monotonic = &*clocks.monotonic()?.abs_clock;
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
&*clocks.monotonic,
|
monotonic,
|
||||||
clocks
|
monotonic
|
||||||
.monotonic
|
.now(monotonic.resolution())
|
||||||
.now(clocks.monotonic.resolution())
|
|
||||||
.checked_add(TIMEOUT)
|
.checked_add(TIMEOUT)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
clocks.monotonic.resolution(),
|
monotonic.resolution(),
|
||||||
Userdata::from(0),
|
Userdata::from(0),
|
||||||
);
|
);
|
||||||
poll_oneoff(&mut poll).await?;
|
poll_oneoff(&mut poll).await?;
|
||||||
@@ -110,9 +110,9 @@ async fn empty_file_writable() -> Result<(), Error> {
|
|||||||
async fn stdio_readable() -> Result<(), Error> {
|
async fn stdio_readable() -> Result<(), Error> {
|
||||||
let clocks = clocks_ctx();
|
let clocks = clocks_ctx();
|
||||||
|
|
||||||
let deadline = clocks
|
let monotonic = &*clocks.monotonic()?.abs_clock;
|
||||||
.monotonic
|
let deadline = monotonic
|
||||||
.now(clocks.monotonic.resolution())
|
.now(monotonic.resolution())
|
||||||
.checked_add(TIMEOUT)
|
.checked_add(TIMEOUT)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -133,10 +133,11 @@ async fn stdio_readable() -> Result<(), Error> {
|
|||||||
poll.subscribe_write(&mut **file, Userdata::from(*ix));
|
poll.subscribe_write(&mut **file, Userdata::from(*ix));
|
||||||
}
|
}
|
||||||
// Timeout bounds time in poll_oneoff
|
// Timeout bounds time in poll_oneoff
|
||||||
|
let monotonic = &*clocks.monotonic()?.abs_clock;
|
||||||
poll.subscribe_monotonic_clock(
|
poll.subscribe_monotonic_clock(
|
||||||
&*clocks.monotonic,
|
monotonic,
|
||||||
deadline,
|
deadline,
|
||||||
clocks.monotonic.resolution(),
|
monotonic.resolution(),
|
||||||
Userdata::from(999),
|
Userdata::from(999),
|
||||||
);
|
);
|
||||||
poll_oneoff(&mut poll).await?;
|
poll_oneoff(&mut poll).await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user