xref: /wasmtime-44.0.1/crates/wasi-io/src/poll.rs (revision 747654b2)
1 use alloc::boxed::Box;
2 use core::any::Any;
3 use core::future::Future;
4 use core::pin::Pin;
5 use wasmtime::Result;
6 use wasmtime::component::{Resource, ResourceTable};
7 
8 pub type DynFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
9 pub type MakeFuture = for<'a> fn(&'a mut dyn Any) -> DynFuture<'a>;
10 
11 /// The host representation of the `wasi:io/poll.pollable` resource.
12 ///
13 /// A pollable is not the same thing as a Rust Future: the same pollable may be used to
14 /// repeatedly check for readiness of a given condition, e.g. if a stream is readable
15 /// or writable. So, rather than containing a Future, which can only become Ready once, a
16 /// `DynPollable` contains a way to create a Future in each call to `poll`.
17 pub struct DynPollable {
18     pub(crate) index: u32,
19     pub(crate) make_future: MakeFuture,
20     pub(crate) remove_index_on_delete: Option<fn(&mut ResourceTable, u32) -> Result<()>>,
21 }
22 
23 /// The trait used to implement [`DynPollable`] to create a `pollable`
24 /// resource in `wasi:io/poll`.
25 ///
26 /// This trait is the internal implementation detail of any pollable resource in
27 /// this crate's implementation of WASI. The `ready` function is an `async fn`
28 /// which resolves when the implementation is ready. Using native `async` Rust
29 /// enables this type's readiness to compose with other types' readiness
30 /// throughout the WASI implementation.
31 ///
32 /// This trait is used in conjunction with [`subscribe`] to create a `pollable`
33 /// resource.
34 ///
35 /// # Example
36 ///
37 /// This is a simple example of creating a `Pollable` resource from a few
38 /// parameters.
39 ///
40 /// ```
41 /// # // stub out so we don't need a dep to build the doctests:
42 /// # mod tokio { pub mod time { pub use std::time::{Duration, Instant}; pub async fn sleep_until(_: Instant) {} } }
43 /// use tokio::time::{self, Duration, Instant};
44 /// use wasmtime_wasi_io::{IoView, poll::{Pollable, subscribe, DynPollable}, async_trait};
45 /// use wasmtime::component::Resource;
46 /// use wasmtime::Result;
47 ///
48 /// fn sleep(cx: &mut dyn IoView, dur: Duration) -> Result<Resource<DynPollable>> {
49 ///     let end = Instant::now() + dur;
50 ///     let sleep = MySleep { end };
51 ///     let sleep_resource = cx.table().push(sleep)?;
52 ///     subscribe(cx.table(), sleep_resource)
53 /// }
54 ///
55 /// struct MySleep {
56 ///     end: Instant,
57 /// }
58 ///
59 /// #[async_trait]
60 /// impl Pollable for MySleep {
61 ///     async fn ready(&mut self) {
62 ///         tokio::time::sleep_until(self.end).await;
63 ///     }
64 /// }
65 /// ```
66 #[async_trait::async_trait]
67 pub trait Pollable: Send + 'static {
68     /// An asynchronous function which resolves when this object's readiness
69     /// operation is ready.
70     ///
71     /// This function is invoked as part of `poll` in `wasi:io/poll`. The
72     /// meaning of when this function Returns depends on what object this
73     /// [`Pollable`] is attached to. When the returned future resolves then the
74     /// corresponding call to `wasi:io/poll` will return.
75     ///
76     /// Note that this method does not return an error. Returning an error
77     /// should be done through accessors on the object that this `pollable` is
78     /// connected to. The call to `wasi:io/poll` itself does not return errors,
79     /// only a list of ready objects.
ready(&mut self)80     async fn ready(&mut self);
81 }
82 
83 /// Creates a `wasi:io/poll.pollable` resource which is subscribed to the provided
84 /// `resource`.
85 ///
86 /// If `resource` is an owned resource then it will be deleted when the returned
87 /// resource is deleted. Otherwise the returned resource is considered a "child"
88 /// of the given `resource` which means that the given resource cannot be
89 /// deleted while the `pollable` is still alive.
subscribe<T>( table: &mut ResourceTable, resource: Resource<T>, ) -> Result<Resource<DynPollable>> where T: Pollable,90 pub fn subscribe<T>(
91     table: &mut ResourceTable,
92     resource: Resource<T>,
93 ) -> Result<Resource<DynPollable>>
94 where
95     T: Pollable,
96 {
97     fn make_future<'a, T>(stream: &'a mut dyn Any) -> DynFuture<'a>
98     where
99         T: Pollable,
100     {
101         stream.downcast_mut::<T>().unwrap().ready()
102     }
103 
104     let pollable = DynPollable {
105         index: resource.rep(),
106         remove_index_on_delete: if resource.owned() {
107             Some(|table, idx| {
108                 let resource = Resource::<T>::new_own(idx);
109                 table.delete(resource)?;
110                 Ok(())
111             })
112         } else {
113             None
114         },
115         make_future: make_future::<T>,
116     };
117 
118     Ok(table.push_child(pollable, &resource)?)
119 }
120