Skip to main content

wasmtime_wasi/p2/host/
clocks.rs

1use crate::clocks::WasiClocksCtxView;
2use crate::p2::DynPollable;
3use crate::p2::bindings::{
4    clocks::monotonic_clock::{self, Duration as WasiDuration, Instant},
5    clocks::wall_clock::{self, Datetime},
6};
7use cap_std::time::SystemTime;
8use std::time::Duration;
9use wasmtime::component::Resource;
10use wasmtime_wasi_io::poll::{Pollable, subscribe};
11
12impl TryFrom<crate::clocks::Datetime> for Datetime {
13    type Error = crate::clocks::DatetimeError;
14
15    fn try_from(
16        crate::clocks::Datetime {
17            seconds,
18            nanoseconds,
19        }: crate::clocks::Datetime,
20    ) -> Result<Self, Self::Error> {
21        Ok(Self {
22            seconds: seconds.try_into()?,
23            nanoseconds,
24        })
25    }
26}
27
28impl TryFrom<Datetime> for crate::clocks::Datetime {
29    type Error = crate::clocks::DatetimeError;
30
31    fn try_from(
32        Datetime {
33            seconds,
34            nanoseconds,
35        }: Datetime,
36    ) -> Result<Self, Self::Error> {
37        Ok(Self {
38            seconds: seconds.try_into()?,
39            nanoseconds,
40        })
41    }
42}
43
44impl TryFrom<SystemTime> for Datetime {
45    type Error = crate::clocks::DatetimeError;
46
47    fn try_from(time: SystemTime) -> Result<Self, Self::Error> {
48        let time = crate::clocks::Datetime::try_from(time)?;
49        time.try_into()
50    }
51}
52
53impl wall_clock::Host for WasiClocksCtxView<'_> {
54    fn now(&mut self) -> wasmtime::Result<Datetime> {
55        let now = self.ctx.wall_clock.now();
56        Ok(Datetime {
57            seconds: now.as_secs(),
58            nanoseconds: now.subsec_nanos(),
59        })
60    }
61
62    fn resolution(&mut self) -> wasmtime::Result<Datetime> {
63        let res = self.ctx.wall_clock.resolution();
64        Ok(Datetime {
65            seconds: res.as_secs(),
66            nanoseconds: res.subsec_nanos(),
67        })
68    }
69}
70
71fn subscribe_to_duration(
72    table: &mut wasmtime::component::ResourceTable,
73    duration: tokio::time::Duration,
74) -> wasmtime::Result<Resource<DynPollable>> {
75    let sleep = if duration.is_zero() {
76        table.push(Deadline::Past)?
77    } else if let Some(deadline) = tokio::time::Instant::now().checked_add(duration) {
78        // NB: this resource created here is not actually exposed to wasm, it's
79        // only an internal implementation detail used to match the signature
80        // expected by `subscribe`.
81        table.push(Deadline::Instant(deadline))?
82    } else {
83        // If the user specifies a time so far in the future we can't
84        // represent it, wait forever rather than trap.
85        table.push(Deadline::Never)?
86    };
87    subscribe(table, sleep)
88}
89
90impl monotonic_clock::Host for WasiClocksCtxView<'_> {
91    fn now(&mut self) -> wasmtime::Result<Instant> {
92        Ok(self.ctx.monotonic_clock.now())
93    }
94
95    fn resolution(&mut self) -> wasmtime::Result<Instant> {
96        Ok(self.ctx.monotonic_clock.resolution())
97    }
98
99    fn subscribe_instant(&mut self, when: Instant) -> wasmtime::Result<Resource<DynPollable>> {
100        let clock_now = self.ctx.monotonic_clock.now();
101        let duration = if when > clock_now {
102            Duration::from_nanos(when - clock_now)
103        } else {
104            Duration::from_nanos(0)
105        };
106        subscribe_to_duration(self.table, duration)
107    }
108
109    fn subscribe_duration(
110        &mut self,
111        duration: WasiDuration,
112    ) -> wasmtime::Result<Resource<DynPollable>> {
113        subscribe_to_duration(self.table, Duration::from_nanos(duration))
114    }
115}
116
117enum Deadline {
118    Past,
119    Instant(tokio::time::Instant),
120    Never,
121}
122
123#[async_trait::async_trait]
124impl Pollable for Deadline {
125    async fn ready(&mut self) {
126        match self {
127            Deadline::Past => {}
128            Deadline::Instant(instant) => tokio::time::sleep_until(*instant).await,
129            Deadline::Never => std::future::pending().await,
130        }
131    }
132}