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