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