wasmtime_wasi/p2/host/
clocks.rs

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