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