From b88f2502cec59188056e3dab4e8c3fe09f03e7ce Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 12 Sep 2023 16:50:44 -0700 Subject: [PATCH] Start to port Wasmtime to the new wasi-io API with resources. This imports the new APIs posted in WebAssembly/wasi-io#46. --- crates/wasi/src/preview2/command.rs | 8 +- crates/wasi/src/preview2/host/clocks.rs | 2 +- crates/wasi/src/preview2/host/io.rs | 4 +- crates/wasi/src/preview2/host/tcp.rs | 2 +- crates/wasi/src/preview2/mod.rs | 10 +- crates/wasi/src/preview2/poll.rs | 18 +- crates/wasi/src/preview2/preview1.rs | 10 +- crates/wasi/wit/command-extended.wit | 2 +- crates/wasi/wit/deps/cli/command.wit | 2 +- .../wasi/wit/deps/clocks/monotonic-clock.wit | 2 +- crates/wasi/wit/deps/http/types.wit | 6 +- crates/wasi/wit/deps/io/poll.wit | 34 ++ crates/wasi/wit/deps/io/streams.wit | 465 +++++++++--------- crates/wasi/wit/deps/poll/poll.wit | 39 -- .../wasi/wit/deps/sockets/ip-name-lookup.wit | 2 +- crates/wasi/wit/deps/sockets/tcp.wit | 2 +- crates/wasi/wit/deps/sockets/udp.wit | 2 +- crates/wasi/wit/main.wit | 2 +- crates/wasi/wit/test.wit | 5 +- 19 files changed, 299 insertions(+), 318 deletions(-) create mode 100644 crates/wasi/wit/deps/io/poll.wit delete mode 100644 crates/wasi/wit/deps/poll/poll.wit diff --git a/crates/wasi/src/preview2/command.rs b/crates/wasi/src/preview2/command.rs index 1ba80b93923c..f83dce6b20cf 100644 --- a/crates/wasi/src/preview2/command.rs +++ b/crates/wasi/src/preview2/command.rs @@ -13,7 +13,7 @@ wasmtime::component::bindgen!({ "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, "wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, "wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, - "wasi:poll/poll": crate::preview2::bindings::poll::poll, + "wasi:io/poll": crate::preview2::bindings::io::poll, "wasi:io/streams": crate::preview2::bindings::io::streams, "wasi:clocks/timezone": crate::preview2::bindings::clocks::timezone, "wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, @@ -37,7 +37,7 @@ pub fn add_to_linker(l: &mut wasmtime::component::Linker) -> any crate::preview2::bindings::clocks::timezone::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::types::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; - crate::preview2::bindings::poll::poll::add_to_linker(l, |t| t)?; + crate::preview2::bindings::io::poll::add_to_linker(l, |t| t)?; crate::preview2::bindings::io::streams::add_to_linker(l, |t| t)?; crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; @@ -73,7 +73,7 @@ pub mod sync { "wasi:filesystem/preopens": crate::preview2::bindings::filesystem::preopens, "wasi:sockets/tcp": crate::preview2::bindings::sockets::tcp, "wasi:clocks/monotonic_clock": crate::preview2::bindings::clocks::monotonic_clock, - "wasi:poll/poll": crate::preview2::bindings::sync_io::poll::poll, + "wasi:io/poll": crate::preview2::bindings::sync_io::io::poll, "wasi:io/streams": crate::preview2::bindings::sync_io::io::streams, "wasi:clocks/timezone": crate::preview2::bindings::clocks::timezone, "wasi:clocks/wall_clock": crate::preview2::bindings::clocks::wall_clock, @@ -99,7 +99,7 @@ pub mod sync { crate::preview2::bindings::clocks::timezone::add_to_linker(l, |t| t)?; crate::preview2::bindings::sync_io::filesystem::types::add_to_linker(l, |t| t)?; crate::preview2::bindings::filesystem::preopens::add_to_linker(l, |t| t)?; - crate::preview2::bindings::sync_io::poll::poll::add_to_linker(l, |t| t)?; + crate::preview2::bindings::sync_io::io::poll::add_to_linker(l, |t| t)?; crate::preview2::bindings::sync_io::io::streams::add_to_linker(l, |t| t)?; crate::preview2::bindings::random::random::add_to_linker(l, |t| t)?; crate::preview2::bindings::cli::exit::add_to_linker(l, |t| t)?; diff --git a/crates/wasi/src/preview2/host/clocks.rs b/crates/wasi/src/preview2/host/clocks.rs index 2c461bd804c8..8c3d762c3790 100644 --- a/crates/wasi/src/preview2/host/clocks.rs +++ b/crates/wasi/src/preview2/host/clocks.rs @@ -4,7 +4,7 @@ use crate::preview2::bindings::{ clocks::monotonic_clock::{self, Instant}, clocks::timezone::{self, Timezone, TimezoneDisplay}, clocks::wall_clock::{self, Datetime}, - poll::poll::Pollable, + io::poll::Pollable, }; use crate::preview2::{HostPollable, TablePollableExt, WasiView}; use cap_std::time::SystemTime; diff --git a/crates/wasi/src/preview2/host/io.rs b/crates/wasi/src/preview2/host/io.rs index ce431af4f542..41a95752c396 100644 --- a/crates/wasi/src/preview2/host/io.rs +++ b/crates/wasi/src/preview2/host/io.rs @@ -1,6 +1,6 @@ use crate::preview2::{ bindings::io::streams::{self, InputStream, OutputStream}, - bindings::poll::poll::Pollable, + bindings::io::poll::Pollable, filesystem::FileInputStream, poll::PollableFuture, stream::{ @@ -404,7 +404,7 @@ pub mod sync { use crate::preview2::{ bindings::io::streams::{self as async_streams, Host as AsyncHost}, bindings::sync_io::io::streams::{self, InputStream, OutputStream}, - bindings::sync_io::poll::poll::Pollable, + bindings::sync_io::io::poll::Pollable, in_tokio, WasiView, }; diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index 89f9bc240207..4ad171cbcb77 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -1,6 +1,6 @@ use crate::preview2::bindings::{ io::streams::{InputStream, OutputStream}, - poll::poll::Pollable, + io::poll::Pollable, sockets::network::{self, ErrorCode, IpAddressFamily, IpSocketAddress, Network}, sockets::tcp::{self, ShutdownType}, }; diff --git a/crates/wasi/src/preview2/mod.rs b/crates/wasi/src/preview2/mod.rs index 5810a05672c5..29d7f014cd4d 100644 --- a/crates/wasi/src/preview2/mod.rs +++ b/crates/wasi/src/preview2/mod.rs @@ -53,7 +53,7 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types ", @@ -67,7 +67,7 @@ pub mod bindings { } }); } - pub use self::_internal::wasi::{filesystem, io, poll}; + pub use self::_internal::wasi::{filesystem, io}; } pub(crate) mod _internal_clocks { @@ -87,7 +87,7 @@ pub mod bindings { wasmtime::component::bindgen!({ path: "wit", interfaces: " - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types ", @@ -102,7 +102,7 @@ pub mod bindings { } }); } - pub use self::_internal_io::wasi::{io, poll}; + pub use self::_internal_io::wasi::io; pub(crate) mod _internal_rest { wasmtime::component::bindgen!({ @@ -134,7 +134,7 @@ pub mod bindings { }, with: { "wasi:clocks/wall-clock": crate::preview2::bindings::clocks::wall_clock, - "wasi:poll/poll": crate::preview2::bindings::poll::poll, + "wasi:io/poll": crate::preview2::bindings::io::poll, "wasi:io/streams": crate::preview2::bindings::io::streams, "wasi:filesystem/types": crate::preview2::bindings::filesystem::types, } diff --git a/crates/wasi/src/preview2/poll.rs b/crates/wasi/src/preview2/poll.rs index a8329b8f2ab7..7c07410d98e2 100644 --- a/crates/wasi/src/preview2/poll.rs +++ b/crates/wasi/src/preview2/poll.rs @@ -1,5 +1,5 @@ use crate::preview2::{ - bindings::poll::poll::{self, Pollable}, + bindings::io::poll::{self, Pollable}, Table, TableError, WasiView, }; use anyhow::Result; @@ -13,12 +13,12 @@ pub type PollableFuture<'a> = Pin> + Send + ' pub type MakeFuture = for<'a> fn(&'a mut dyn Any) -> PollableFuture<'a>; pub type ClosureFuture = Box PollableFuture<'static> + Send + Sync + 'static>; -/// A host representation of the `wasi:poll/poll.pollable` resource. +/// A 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 -/// HostPollable contains a way to create a Future in each call to poll_oneoff. +/// HostPollable contains a way to create a Future in each call to `poll_list`. pub enum HostPollable { /// Create a Future by calling a fn on another resource in the table. This /// indirection means the created Future can use a mut borrow of another @@ -58,7 +58,7 @@ impl poll::Host for T { Ok(()) } - async fn poll_oneoff(&mut self, pollables: Vec) -> Result> { + async fn poll_list(&mut self, pollables: Vec) -> Result> { type ReadylistIndex = usize; let table = self.table_mut(); @@ -107,7 +107,7 @@ impl poll::Host for T { } Poll::Ready(Err(e)) => { return Poll::Ready(Err( - e.context(format!("poll_oneoff {readylist_indicies:?}")) + e.context(format!("poll_list {readylist_indicies:?}")) )); } Poll::Pending => {} @@ -130,8 +130,8 @@ impl poll::Host for T { pub mod sync { use crate::preview2::{ - bindings::poll::poll::Host as AsyncHost, - bindings::sync_io::poll::poll::{self, Pollable}, + bindings::io::poll::Host as AsyncHost, + bindings::sync_io::io::poll::{self, Pollable}, in_tokio, WasiView, }; use anyhow::Result; @@ -141,8 +141,8 @@ pub mod sync { in_tokio(async { AsyncHost::drop_pollable(self, pollable).await }) } - fn poll_oneoff(&mut self, pollables: Vec) -> Result> { - in_tokio(async { AsyncHost::poll_oneoff(self, pollables).await }) + fn poll_list(&mut self, pollables: Vec) -> Result> { + in_tokio(async { AsyncHost::poll_list(self, pollables).await }) } } } diff --git a/crates/wasi/src/preview2/preview1.rs b/crates/wasi/src/preview2/preview1.rs index b45955063b50..a46e8bf4c796 100644 --- a/crates/wasi/src/preview2/preview1.rs +++ b/crates/wasi/src/preview2/preview1.rs @@ -5,7 +5,7 @@ use crate::preview2::bindings::cli::{ use crate::preview2::bindings::clocks::{monotonic_clock, wall_clock}; use crate::preview2::bindings::filesystem::{preopens, types as filesystem}; use crate::preview2::bindings::io::streams; -use crate::preview2::bindings::poll; +use crate::preview2::bindings::io::poll; use crate::preview2::filesystem::TableFsExt; use crate::preview2::host::filesystem::TableReaddirExt; use crate::preview2::{bindings, IsATTY, TableError, WasiView}; @@ -69,7 +69,7 @@ impl BlockingMode { } async fn write( &self, - host: &mut (impl streams::Host + poll::poll::Host), + host: &mut (impl streams::Host + poll::Host), output_stream: streams::OutputStream, mut bytes: &[u8], ) -> Result { @@ -498,7 +498,7 @@ wiggle::from_witx!({ wasi_snapshot_preview1::{ fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size, fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write, - fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get, + fd_pwrite, poll_list, path_create_directory, path_filestat_get, path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory, path_rename, path_symlink, path_unlink_file } @@ -517,7 +517,7 @@ mod sync { wasi_snapshot_preview1::{ fd_advise, fd_close, fd_datasync, fd_fdstat_get, fd_filestat_get, fd_filestat_set_size, fd_filestat_set_times, fd_read, fd_pread, fd_seek, fd_sync, fd_readdir, fd_write, - fd_pwrite, poll_oneoff, path_create_directory, path_filestat_get, + fd_pwrite, poll_list, path_create_directory, path_filestat_get, path_filestat_set_times, path_link, path_open, path_readlink, path_remove_directory, path_rename, path_symlink, path_unlink_file } @@ -849,7 +849,7 @@ impl< + bindings::cli::exit::Host + bindings::filesystem::preopens::Host + bindings::filesystem::types::Host - + bindings::poll::poll::Host + + bindings::io::poll::Host + bindings::random::random::Host + bindings::io::streams::Host + bindings::clocks::monotonic_clock::Host diff --git a/crates/wasi/wit/command-extended.wit b/crates/wasi/wit/command-extended.wit index 314e26dfce1b..3c56808e4abe 100644 --- a/crates/wasi/wit/command-extended.wit +++ b/crates/wasi/wit/command-extended.wit @@ -16,7 +16,7 @@ world command-extended { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi/wit/deps/cli/command.wit b/crates/wasi/wit/deps/cli/command.wit index 3cd17bea3f98..5ca4e2446afd 100644 --- a/crates/wasi/wit/deps/cli/command.wit +++ b/crates/wasi/wit/deps/cli/command.wit @@ -16,7 +16,7 @@ world command { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import environment diff --git a/crates/wasi/wit/deps/clocks/monotonic-clock.wit b/crates/wasi/wit/deps/clocks/monotonic-clock.wit index 50eb4de111af..30515f4bf6af 100644 --- a/crates/wasi/wit/deps/clocks/monotonic-clock.wit +++ b/crates/wasi/wit/deps/clocks/monotonic-clock.wit @@ -11,7 +11,7 @@ package wasi:clocks /// /// It is intended for measuring elapsed time. interface monotonic-clock { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} /// A timestamp in nanoseconds. type instant = u64 diff --git a/crates/wasi/wit/deps/http/types.wit b/crates/wasi/wit/deps/http/types.wit index 7b7b015529c0..30d976c441c0 100644 --- a/crates/wasi/wit/deps/http/types.wit +++ b/crates/wasi/wit/deps/http/types.wit @@ -3,7 +3,7 @@ // imported and exported interfaces. interface types { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} // This type corresponds to HTTP standard Methods. variant method { @@ -95,7 +95,7 @@ interface types { // Additional optional parameters that can be set when making a request. record request-options { // The following timeouts are specific to the HTTP protocol and work - // independently of the overall timeouts passed to `io.poll.poll-oneoff`. + // independently of the overall timeouts passed to `io.poll.poll-list`. // The timeout for the initial connect. connect-timeout-ms: option, @@ -147,7 +147,7 @@ interface types { // `future-incoming-response`, the client can call the non-blocking `get` // method to get the result if it is available. If the result is not available, // the client can call `listen` to get a `pollable` that can be passed to - // `io.poll.poll-oneoff`. + // `wasi:io/poll.poll-list`. type future-incoming-response = u32 drop-future-incoming-response: func(f: future-incoming-response) future-incoming-response-get: func(f: future-incoming-response) -> option> diff --git a/crates/wasi/wit/deps/io/poll.wit b/crates/wasi/wit/deps/io/poll.wit new file mode 100644 index 000000000000..61ab432ec174 --- /dev/null +++ b/crates/wasi/wit/deps/io/poll.wit @@ -0,0 +1,34 @@ +package wasi:io + +/// A poll API intended to let users wait for I/O events on multiple handles +/// at once. +interface poll { + /// A "pollable" handle. + resource pollable + + /// Poll for completion on a set of pollables. + /// + /// This function takes a list of pollables, which identify I/O sources of + /// interest, and waits until one or more of the events is ready for I/O. + /// + /// The result `list` contains one or more indices of handles in the + /// argument list that is ready for I/O. + /// + /// If the list contains more elements than can be indexed with a `u32` + /// value, this function traps. + /// + /// A timeout can be implemented by adding a pollable from the + /// wasi-clocks API to the list. + /// + /// This function does not return a `result`; polling in itself does not + /// do any I/O so it doesn't fail. If any of the I/O sources identified by + /// the pollables has an error, it is indicated by marking the source as + /// being reaedy for I/O. + poll-list: func(in: list) -> list + + /// Poll for completion on a single pollable. + /// + /// This function is similar to `poll-list`, but operates on only a single + /// pollable. When it returns, the handle is ready for I/O. + poll-one: func(in: pollable) +} diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit index e2631f66a569..dbe052bdf400 100644 --- a/crates/wasi/wit/deps/io/streams.wit +++ b/crates/wasi/wit/deps/io/streams.wit @@ -6,7 +6,7 @@ package wasi:io /// In the future, the component model is expected to add built-in stream types; /// when it does, they are expected to subsume this API. interface streams { - use wasi:poll/poll.{pollable} + use poll.{pollable} /// Streams provide a sequence of data and then end; once they end, they /// no longer provide any further data. @@ -24,115 +24,81 @@ interface streams { ended, } - /// An input bytestream. In the future, this will be replaced by handle - /// types. + /// An input bytestream. /// /// `input-stream`s are *non-blocking* to the extent practical on underlying /// platforms. I/O operations always return promptly; if fewer bytes are /// promptly available than requested, they return the number of bytes promptly /// available, which could even be zero. To wait for data to be available, - /// use the `subscribe-to-input-stream` function to obtain a `pollable` which - /// can be polled for using `wasi:poll/poll.poll_oneoff`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type input-stream = u32 - - /// Perform a non-blocking read from the stream. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a `stream-status` which, indicates whether further - /// reads are expected to produce data. The returned list will contain up to - /// `len` bytes; it may return fewer than requested, but not more. An - /// empty list and `stream-status:open` indicates no more data is - /// available at this time, and that the pollable given by - /// `subscribe-to-input-stream` will be ready when more data is available. - /// - /// Once a stream has reached the end, subsequent calls to `read` or - /// `skip` will always report `stream-status:ended` rather than producing more - /// data. - /// - /// When the caller gives a `len` of 0, it represents a request to read 0 - /// bytes. This read should always succeed and return an empty list and - /// the current `stream-status`. - /// - /// The `len` parameter is a `u64`, which could represent a list of u8 which - /// is not possible to allocate in wasm32, or not desirable to allocate as - /// as a return value by the callee. The callee may return a list of bytes - /// less than `len` in size while more bytes are available for reading. - read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Read bytes from a stream, after blocking until at least one byte can - /// be read. Except for blocking, identical to `read`. - blocking-read: func( - this: input-stream, - /// The maximum number of bytes to read - len: u64 - ) -> result, stream-status>> - - /// Skip bytes from a stream. - /// - /// This is similar to the `read` function, but avoids copying the - /// bytes into the instance. - /// - /// Once a stream has reached the end, subsequent calls to read or - /// `skip` will always report end-of-stream rather than producing more - /// data. - /// - /// This function returns the number of bytes skipped, along with a - /// `stream-status` indicating whether the end of the stream was - /// reached. The returned value will be at most `len`; it may be less. - skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// use the `subscribe` function to obtain a `pollable` which can be polled + /// for using `wasi:io/poll`. + resource input-stream { + /// Perform a non-blocking read from the stream. + /// + /// This function returns a list of bytes containing the data that was + /// read, along with a `stream-status` which, indicates whether further + /// reads are expected to produce data. The returned list will contain up to + /// `len` bytes; it may return fewer than requested, but not more. An + /// empty list and `stream-status:open` indicates no more data is + /// available at this time, and that the pollable given by `subscribe` + /// will be ready when more data is available. + /// + /// Once a stream has reached the end, subsequent calls to `read` or + /// `skip` will always report `stream-status:ended` rather than producing more + /// data. + /// + /// When the caller gives a `len` of 0, it represents a request to read 0 + /// bytes. This read should always succeed and return an empty list and + /// the current `stream-status`. + /// + /// The `len` parameter is a `u64`, which could represent a list of u8 which + /// is not possible to allocate in wasm32, or not desirable to allocate as + /// as a return value by the callee. The callee may return a list of bytes + /// less than `len` in size while more bytes are available for reading. + read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Skip bytes from a stream, after blocking until at least one byte - /// can be skipped. Except for blocking behavior, identical to `skip`. - blocking-skip: func( - this: input-stream, - /// The maximum number of bytes to skip. - len: u64, - ) -> result> + /// Read bytes from a stream, after blocking until at least one byte can + /// be read. Except for blocking, identical to `read`. + blocking-read: func( + /// The maximum number of bytes to read + len: u64 + ) -> result, stream-status>> - /// Create a `pollable` which will resolve once either the specified stream - /// has bytes available to read or the other end of the stream has been - /// closed. - /// The created `pollable` is a child resource of the `input-stream`. - /// Implementations may trap if the `input-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-input-stream: func(this: input-stream) -> pollable + /// Skip bytes from a stream. + /// + /// This is similar to the `read` function, but avoids copying the + /// bytes into the instance. + /// + /// Once a stream has reached the end, subsequent calls to read or + /// `skip` will always report end-of-stream rather than producing more + /// data. + /// + /// This function returns the number of bytes skipped, along with a + /// `stream-status` indicating whether the end of the stream was + /// reached. The returned value will be at most `len`; it may be less. + skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// Dispose of the specified `input-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `input-stream` is dropped while child - /// `pollable` resources are still alive. - /// After this `input-stream` is dropped, implementations may report any - /// corresponding `output-stream` has `stream-state.closed`. - drop-input-stream: func(this: input-stream) + /// Skip bytes from a stream, after blocking until at least one byte + /// can be skipped. Except for blocking behavior, identical to `skip`. + blocking-skip: func( + /// The maximum number of bytes to skip. + len: u64, + ) -> result> - /// An output bytestream. In the future, this will be replaced by handle - /// types. - /// - /// `output-stream`s are *non-blocking* to the extent practical on - /// underlying platforms. Except where specified otherwise, I/O operations also - /// always return promptly, after the number of bytes that can be written - /// promptly, which could even be zero. To wait for the stream to be ready to - /// accept data, the `subscribe-to-output-stream` function to obtain a - /// `pollable` which can be polled for using `wasi:poll`. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type output-stream = u32 + /// Create a `pollable` which will resolve once either the specified stream + /// has bytes available to read or the other end of the stream has been + /// closed. + /// The created `pollable` is a child resource of the `input-stream`. + /// Implementations may trap if the `input-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable + } /// An error for output-stream operations. /// @@ -146,155 +112,174 @@ interface streams { /// future operations. closed } - /// Check readiness for writing. This function never blocks. - /// - /// Returns the number of bytes permitted for the next call to `write`, - /// or an error. Calling `write` with more bytes than this function has - /// permitted will trap. - /// - /// When this function returns 0 bytes, the `subscribe-to-output-stream` - /// pollable will become ready when this function will report at least - /// 1 byte, or an error. - check-write: func( - this: output-stream - ) -> result - /// Perform a write. This function never blocks. - /// - /// Precondition: check-write gave permit of Ok(n) and contents has a - /// length of less than or equal to n. Otherwise, this function will trap. + /// An output bytestream. /// - /// returns Err(closed) without writing if the stream has closed since - /// the last call to check-write provided a permit. - write: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// `output-stream`s are *non-blocking* to the extent practical on + /// underlying platforms. Except where specified otherwise, I/O operations also + /// always return promptly, after the number of bytes that can be written + /// promptly, which could even be zero. To wait for the stream to be ready to + /// accept data, the `subscribe` function to obtain a `pollable` which can be + /// polled for using `wasi:io/poll`. + resource output-stream { + /// Check readiness for writing. This function never blocks. + /// + /// Returns the number of bytes permitted for the next call to `write`, + /// or an error. Calling `write` with more bytes than this function has + /// permitted will trap. + /// + /// When this function returns 0 bytes, the `subscribe` pollable will + /// become ready when this function will report at least 1 byte, or an + /// error. + check-write: func() -> result - /// Perform a write of up to 4096 bytes, and then flush the stream. Block - /// until all of these operations are complete, or an error occurs. - /// - /// This is a convenience wrapper around the use of `check-write`, - /// `subscribe-to-output-stream`, `write`, and `flush`, and is implemented - /// with the following pseudo-code: - /// - /// ```text - /// let pollable = subscribe-to-output-stream(this); - /// while !contents.is_empty() { - /// // Wait for the stream to become writable - /// poll-oneoff(pollable); - /// let Ok(n) = check-write(this); // eliding error handling - /// let len = min(n, contents.len()); - /// let (chunk, rest) = contents.split_at(len); - /// write(this, chunk); // eliding error handling - /// contents = rest; - /// } - /// flush(this); - /// // Wait for completion of `flush` - /// poll-oneoff(pollable); - /// // Check for any errors that arose during `flush` - /// let _ = check-write(this); // eliding error handling - /// ``` - blocking-write-and-flush: func( - this: output-stream, - contents: list - ) -> result<_, write-error> + /// Perform a write. This function never blocks. + /// + /// Precondition: check-write gave permit of Ok(n) and contents has a + /// length of less than or equal to n. Otherwise, this function will trap. + /// + /// returns Err(closed) without writing if the stream has closed since + /// the last call to check-write provided a permit. + write: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output. This function never blocks. - /// - /// This tells the output-stream that the caller intends any buffered - /// output to be flushed. the output which is expected to be flushed - /// is all that has been passed to `write` prior to this call. - /// - /// Upon calling this function, the `output-stream` will not accept any - /// writes (`check-write` will return `ok(0)`) until the flush has - /// completed. The `subscribe-to-output-stream` pollable will become ready - /// when the flush has completed and the stream can accept more writes. - flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Perform a write of up to 4096 bytes, and then flush the stream. Block + /// until all of these operations are complete, or an error occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write`, and `flush`, and is implemented with the + /// following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while !contents.is_empty() { + /// // Wait for the stream to become writable + /// poll-one(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, contents.len()); + /// let (chunk, rest) = contents.split_at(len); + /// this.write(chunk ); // eliding error handling + /// contents = rest; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-list(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-and-flush: func( + contents: list + ) -> result<_, write-error> - /// Request to flush buffered output, and block until flush completes - /// and stream is ready for writing again. - blocking-flush: func( - this: output-stream, - ) -> result<_, write-error> + /// Request to flush buffered output. This function never blocks. + /// + /// This tells the output-stream that the caller intends any buffered + /// output to be flushed. the output which is expected to be flushed + /// is all that has been passed to `write` prior to this call. + /// + /// Upon calling this function, the `output-stream` will not accept any + /// writes (`check-write` will return `ok(0)`) until the flush has + /// completed. The `subscribe` pollable will become ready when the + /// flush has completed and the stream can accept more writes. + flush: func() -> result<_, write-error> - /// Create a `pollable` which will resolve once the output-stream - /// is ready for more writing, or an error has occured. When this - /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an - /// error. - /// - /// If the stream is closed, this pollable is always ready immediately. - /// - /// The created `pollable` is a child resource of the `output-stream`. - /// Implementations may trap if the `output-stream` is dropped before - /// all derived `pollable`s created with this function are dropped. - subscribe-to-output-stream: func(this: output-stream) -> pollable + /// Request to flush buffered output, and block until flush completes + /// and stream is ready for writing again. + blocking-flush: func() -> result<_, write-error> - /// Write zeroes to a stream. - /// - /// this should be used precisely like `write` with the exact same - /// preconditions (must use check-write first), but instead of - /// passing a list of bytes, you simply pass the number of zero-bytes - /// that should be written. - write-zeroes: func( - this: output-stream, - /// The number of zero-bytes to write - len: u64 - ) -> result<_, write-error> + /// Create a `pollable` which will resolve once the output-stream + /// is ready for more writing, or an error has occured. When this + /// pollable is ready, `check-write` will return `ok(n)` with n>0, or an + /// error. + /// + /// If the stream is closed, this pollable is always ready immediately. + /// + /// The created `pollable` is a child resource of the `output-stream`. + /// Implementations may trap if the `output-stream` is dropped before + /// all derived `pollable`s created with this function are dropped. + subscribe: func() -> pollable - /// Read from one stream and write to another. - /// - /// This function returns the number of bytes transferred; it may be less - /// than `len`. - /// - /// Unlike other I/O functions, this function blocks until all the data - /// read from the input stream has been written to the output stream. - splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Write zeroes to a stream. + /// + /// this should be used precisely like `write` with the exact same + /// preconditions (must use check-write first), but instead of + /// passing a list of bytes, you simply pass the number of zero-bytes + /// that should be written. + write-zeroes: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Read from one stream and write to another, with blocking. - /// - /// This is similar to `splice`, except that it blocks until at least - /// one byte can be read. - blocking-splice: func( - this: output-stream, - /// The stream to read from - src: input-stream, - /// The number of bytes to splice - len: u64, - ) -> result> + /// Perform a write of up to 4096 zeroes, and then flush the stream. + /// Block until all of these operations are complete, or an error + /// occurs. + /// + /// This is a convenience wrapper around the use of `check-write`, + /// `subscribe`, `write-zeroes`, and `flush`, and is implemented with + /// the following pseudo-code: + /// + /// ```text + /// let pollable = this.subscribe(); + /// while num_zeroes != 0 { + /// // Wait for the stream to become writable + /// poll-list(pollable); + /// let Ok(n) = this.check-write(); // eliding error handling + /// let len = min(n, num_zeroes); + /// this.write-zeroes(len); // eliding error handling + /// num_zeroes -= len; + /// } + /// this.flush(); + /// // Wait for completion of `flush` + /// poll-list(pollable); + /// // Check for any errors that arose during `flush` + /// let _ = this.check-write(); // eliding error handling + /// ``` + blocking-write-zeroes-and-flush: func( + /// The number of zero-bytes to write + len: u64 + ) -> result<_, write-error> - /// Forward the entire contents of an input stream to an output stream. - /// - /// This function repeatedly reads from the input stream and writes - /// the data to the output stream, until the end of the input stream - /// is reached, or an error is encountered. - /// - /// Unlike other I/O functions, this function blocks until the end - /// of the input stream is seen and all the data has been written to - /// the output stream. - /// - /// This function returns the number of bytes transferred, and the status of - /// the output stream. - forward: func( - this: output-stream, - /// The stream to read from - src: input-stream - ) -> result> + /// Read from one stream and write to another. + /// + /// This function returns the number of bytes transferred; it may be less + /// than `len`. + /// + /// Unlike other I/O functions, this function blocks until all the data + /// read from the input stream has been written to the output stream. + splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> + /// Read from one stream and write to another, with blocking. + /// + /// This is similar to `splice`, except that it blocks until at least + /// one byte can be read. + blocking-splice: func( + /// The stream to read from + src: input-stream, + /// The number of bytes to splice + len: u64, + ) -> result> - /// Dispose of the specified `output-stream`, after which it may no longer - /// be used. - /// Implementations may trap if this `output-stream` is dropped while - /// child `pollable` resources are still alive. - /// After this `output-stream` is dropped, implementations may report any - /// corresponding `input-stream` has `stream-state.closed`. - drop-output-stream: func(this: output-stream) + /// Forward the entire contents of an input stream to an output stream. + /// + /// This function repeatedly reads from the input stream and writes + /// the data to the output stream, until the end of the input stream + /// is reached, or an error is encountered. + /// + /// Unlike other I/O functions, this function blocks until the end + /// of the input stream is seen and all the data has been written to + /// the output stream. + /// + /// This function returns the number of bytes transferred, and the status of + /// the output stream. + forward: func( + /// The stream to read from + src: input-stream + ) -> result> + } } diff --git a/crates/wasi/wit/deps/poll/poll.wit b/crates/wasi/wit/deps/poll/poll.wit deleted file mode 100644 index a6334c5570fc..000000000000 --- a/crates/wasi/wit/deps/poll/poll.wit +++ /dev/null @@ -1,39 +0,0 @@ -package wasi:poll - -/// A poll API intended to let users wait for I/O events on multiple handles -/// at once. -interface poll { - /// A "pollable" handle. - /// - /// This is conceptually represents a `stream<_, _>`, or in other words, - /// a stream that one can wait on, repeatedly, but which does not itself - /// produce any data. It's temporary scaffolding until component-model's - /// async features are ready. - /// - /// And at present, it is a `u32` instead of being an actual handle, until - /// the wit-bindgen implementation of handles and resources is ready. - /// - /// `pollable` lifetimes are not automatically managed. Users must ensure - /// that they do not outlive the resource they reference. - /// - /// This [represents a resource](https://github.com/WebAssembly/WASI/blob/main/docs/WitInWasi.md#Resources). - type pollable = u32 - - /// Dispose of the specified `pollable`, after which it may no longer - /// be used. - drop-pollable: func(this: pollable) - - /// Poll for completion on a set of pollables. - /// - /// The "oneoff" in the name refers to the fact that this function must do a - /// linear scan through the entire list of subscriptions, which may be - /// inefficient if the number is large and the same subscriptions are used - /// many times. In the future, this is expected to be obsoleted by the - /// component model async proposal, which will include a scalable waiting - /// facility. - /// - /// The result list is the same length as the argument - /// list, and indicates the readiness of each corresponding - /// element in that / list, with true indicating ready. - poll-oneoff: func(in: list) -> list -} diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit index f15d19d037da..131862ca1116 100644 --- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit +++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit @@ -1,6 +1,6 @@ interface ip-name-lookup { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-address, ip-address-family} diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index 3922769b308e..631483fdc4af 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -1,7 +1,7 @@ interface tcp { use wasi:io/streams.{input-stream, output-stream} - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} /// A TCP socket handle. diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit index 700b9e247692..d6ac1600bcd9 100644 --- a/crates/wasi/wit/deps/sockets/udp.wit +++ b/crates/wasi/wit/deps/sockets/udp.wit @@ -1,6 +1,6 @@ interface udp { - use wasi:poll/poll.{pollable} + use wasi:io/poll.{pollable} use network.{network, error-code, ip-socket-address, ip-address-family} diff --git a/crates/wasi/wit/main.wit b/crates/wasi/wit/main.wit index 753770ad22ab..739e1bd4ac48 100644 --- a/crates/wasi/wit/main.wit +++ b/crates/wasi/wit/main.wit @@ -18,7 +18,7 @@ world preview1-adapter-reactor { import wasi:random/random import wasi:random/insecure import wasi:random/insecure-seed - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/exit diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit index 4543cb194af1..0b6bd28e997d 100644 --- a/crates/wasi/wit/test.wit +++ b/crates/wasi/wit/test.wit @@ -2,6 +2,7 @@ world test-reactor { import wasi:cli/environment + import wasi:io/poll import wasi:io/streams import wasi:filesystem/types import wasi:filesystem/preopens @@ -19,7 +20,7 @@ world test-reactor { } world test-command { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin @@ -28,7 +29,7 @@ world test-command { } world test-command-with-sockets { - import wasi:poll/poll + import wasi:io/poll import wasi:io/streams import wasi:cli/environment import wasi:cli/stdin