wasmtime_wasi/host/
clocks.rs

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