From 2d6db7e61c0199703e8fb6e9ffb988ae2273b67a Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 14:01:07 -0800
Subject: [PATCH 01/27] stub: wasmtime-wasi-io crate
---
Cargo.lock | 12 ++++++++++++
Cargo.toml | 3 ++-
crates/wasi-io/Cargo.toml | 26 ++++++++++++++++++++++++++
crates/wasi-io/src/lib.rs | 0
crates/wasi/Cargo.toml | 1 +
5 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 crates/wasi-io/Cargo.toml
create mode 100644 crates/wasi-io/src/lib.rs
diff --git a/Cargo.lock b/Cargo.lock
index bfb0a2df0331..923ce289ca7d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4430,6 +4430,7 @@ dependencies = [
"tracing-subscriber",
"url",
"wasmtime",
+ "wasmtime-wasi-io",
"wiggle",
"windows-sys 0.59.0",
]
@@ -4471,6 +4472,17 @@ dependencies = [
"webpki-roots",
]
+[[package]]
+name = "wasmtime-wasi-io"
+version = "30.0.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "bytes",
+ "futures",
+ "wasmtime",
+]
+
[[package]]
name = "wasmtime-wasi-keyvalue"
version = "30.0.0"
diff --git a/Cargo.toml b/Cargo.toml
index 88c88bf7a6ef..2c8ca3412515 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -230,6 +230,7 @@ wasmtime-fiber = { path = "crates/fiber", version = "=30.0.0" }
wasmtime-jit-debug = { path = "crates/jit-debug", version = "=30.0.0" }
wasmtime-wast = { path = "crates/wast", version = "=30.0.0" }
wasmtime-wasi = { path = "crates/wasi", version = "30.0.0", default-features = false }
+wasmtime-wasi-io = { path = "crates/wasi-io", version = "30.0.0", default-features = false }
wasmtime-wasi-http = { path = "crates/wasi-http", version = "=30.0.0", default-features = false }
wasmtime-wasi-nn = { path = "crates/wasi-nn", version = "30.0.0" }
wasmtime-wasi-config = { path = "crates/wasi-config", version = "30.0.0" }
@@ -355,7 +356,7 @@ hyper = "1.0.1"
http = "1.0.0"
http-body = "1.0.0"
http-body-util = "0.1.0"
-bytes = "1.4"
+bytes = { version = "1.4", default-features = false }
futures = { version = "0.3.27", default-features = false }
indexmap = { version = "2.0.0", default-features = false }
pretty_env_logger = "0.5.0"
diff --git a/crates/wasi-io/Cargo.toml b/crates/wasi-io/Cargo.toml
new file mode 100644
index 000000000000..7dd182383e07
--- /dev/null
+++ b/crates/wasi-io/Cargo.toml
@@ -0,0 +1,26 @@
+[package]
+name = "wasmtime-wasi-io"
+version.workspace = true
+authors.workspace = true
+description = "wasi-io common traits to be shared among other wasi implementations"
+license = "Apache-2.0 WITH LLVM-exception"
+categories = ["wasm"]
+keywords = ["webassembly", "wasm"]
+repository = "https://github.com/bytecodealliance/wasmtime"
+edition.workspace = true
+rust-version.workspace = true
+
+[lints]
+workspace = true
+
+[dependencies]
+wasmtime = { workspace = true, features = ["component-model", "async", "runtime"] }
+anyhow = { workspace = true, default-features = false }
+bytes = { workspace = true, default-features = false }
+async-trait = { workspace = true }
+futures = { workspace = true }
+
+[features]
+default = [ "std"]
+std = []
+
diff --git a/crates/wasi-io/src/lib.rs b/crates/wasi-io/src/lib.rs
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml
index db02061ce315..e18afd81c2c0 100644
--- a/crates/wasi/Cargo.toml
+++ b/crates/wasi/Cargo.toml
@@ -17,6 +17,7 @@ workspace = true
[dependencies]
wasmtime = { workspace = true, features = ["component-model", "async", "runtime", "std"] }
+wasmtime-wasi-io = { workspace = true, features = ["std"] }
anyhow = { workspace = true }
wiggle = { workspace = true, optional = true, features = ["wasmtime"] }
tokio = { workspace = true, features = ["time", "sync", "io-std", "io-util", "rt", "rt-multi-thread", "net"] }
From f475c0f35cde990b678e380f54fbdf89216e1394 Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 16:32:13 -0800
Subject: [PATCH 02/27] wasmtime: component::ResourceTableError now impls
core::error::Error
for compatibility without std
---
crates/wasmtime/src/runtime/component/resource_table.rs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/crates/wasmtime/src/runtime/component/resource_table.rs b/crates/wasmtime/src/runtime/component/resource_table.rs
index cceb65c20cd4..d2e65536d358 100644
--- a/crates/wasmtime/src/runtime/component/resource_table.rs
+++ b/crates/wasmtime/src/runtime/component/resource_table.rs
@@ -29,8 +29,7 @@ impl fmt::Display for ResourceTableError {
}
}
-#[cfg(feature = "std")]
-impl std::error::Error for ResourceTableError {}
+impl core::error::Error for ResourceTableError {}
/// The `ResourceTable` type maps a `Resource` to its `T`.
#[derive(Debug)]
From 48ef95f49b7a86d5c8db702cb8f74fbbe77540a5 Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 16:40:17 -0800
Subject: [PATCH 03/27] relocate much of the wasi-io impl into wasmtime-wasi-io
---
ci/vendor-wit.sh | 4 +
crates/wasi-io/Cargo.toml | 12 +-
crates/wasi-io/src/lib.rs | 36 +++
crates/{wasi => wasi-io}/src/poll.rs | 40 +---
crates/{wasi => wasi-io}/src/stream.rs | 0
crates/wasi-io/src/view.rs | 40 ++++
crates/wasi-io/wit/deps/io/error.wit | 34 +++
crates/wasi-io/wit/deps/io/poll.wit | 47 ++++
crates/wasi-io/wit/deps/io/streams.wit | 290 +++++++++++++++++++++++++
crates/wasi-io/wit/deps/io/world.wit | 10 +
crates/wasi-io/wit/world.wit | 6 +
crates/wasi/src/lib.rs | 12 +-
crates/wasi/src/view.rs | 41 +---
13 files changed, 486 insertions(+), 86 deletions(-)
rename crates/{wasi => wasi-io}/src/poll.rs (85%)
rename crates/{wasi => wasi-io}/src/stream.rs (100%)
create mode 100644 crates/wasi-io/src/view.rs
create mode 100644 crates/wasi-io/wit/deps/io/error.wit
create mode 100644 crates/wasi-io/wit/deps/io/poll.wit
create mode 100644 crates/wasi-io/wit/deps/io/streams.wit
create mode 100644 crates/wasi-io/wit/deps/io/world.wit
create mode 100644 crates/wasi-io/wit/world.wit
diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh
index 252fb9a863e5..b71ca579a70a 100755
--- a/ci/vendor-wit.sh
+++ b/ci/vendor-wit.sh
@@ -36,6 +36,10 @@ make_vendor() {
cache_dir=$(mktemp -d)
+make_vendor "wasi-io" "
+ io@v0.2.3
+"
+
make_vendor "wasi" "
cli@v0.2.3
clocks@v0.2.3
diff --git a/crates/wasi-io/Cargo.toml b/crates/wasi-io/Cargo.toml
index 7dd182383e07..70e379ea6a8f 100644
--- a/crates/wasi-io/Cargo.toml
+++ b/crates/wasi-io/Cargo.toml
@@ -15,12 +15,16 @@ workspace = true
[dependencies]
wasmtime = { workspace = true, features = ["component-model", "async", "runtime"] }
-anyhow = { workspace = true, default-features = false }
-bytes = { workspace = true, default-features = false }
+anyhow = { workspace = true }
+bytes = { workspace = true }
async-trait = { workspace = true }
futures = { workspace = true }
[features]
-default = [ "std"]
-std = []
+default = [ "std" ]
+std = [
+ "bytes/std",
+ "anyhow/std",
+ "wasmtime/std",
+]
diff --git a/crates/wasi-io/src/lib.rs b/crates/wasi-io/src/lib.rs
index e69de29bb2d1..cbb457f90548 100644
--- a/crates/wasi-io/src/lib.rs
+++ b/crates/wasi-io/src/lib.rs
@@ -0,0 +1,36 @@
+pub mod poll;
+pub mod stream;
+mod view;
+
+pub use view::{IoImpl, IoView};
+
+pub mod bindings {
+ wasmtime::component::bindgen!({
+ path: "wit",
+ trappable_imports: true,
+ with: {
+ "wasi:io/poll/pollable": crate::poll::Pollable,
+ "wasi:io/streams/input-stream": crate::stream::InputStream,
+ "wasi:io/streams/output-stream": crate::stream::OutputStream,
+ },
+ async: {
+ only_imports: [
+ "poll",
+ "[method]pollable.block",
+ "[method]pollable.ready",
+ "[method]input-stream.blocking-read",
+ "[method]input-stream.blocking-skip",
+ "[drop]input-stream",
+ "[method]output-stream.blocking-splice",
+ "[method]output-stream.blocking-flush",
+ "[method]output-stream.blocking-write",
+ "[method]output-stream.blocking-write-and-flush",
+ "[method]output-stream.blocking-write-zeroes-and-flush",
+ "[drop]output-stream",
+ ]
+ },
+ trappable_error_type: {
+ "wasi:io/streams/stream-error" => crate::stream::StreamError,
+ }
+ });
+}
diff --git a/crates/wasi/src/poll.rs b/crates/wasi-io/src/poll.rs
similarity index 85%
rename from crates/wasi/src/poll.rs
rename to crates/wasi-io/src/poll.rs
index 0d774b9b5e30..adf36a292ac6 100644
--- a/crates/wasi/src/poll.rs
+++ b/crates/wasi-io/src/poll.rs
@@ -1,4 +1,5 @@
-use crate::{bindings::io::poll, IoImpl, IoView};
+use crate::bindings::wasi::io::poll;
+use crate::view::{IoImpl, IoView};
use anyhow::{anyhow, Result};
use std::any::Any;
use std::collections::HashMap;
@@ -177,7 +178,7 @@ where
}
}
-impl crate::bindings::io::poll::HostPollable for IoImpl
+impl crate::bindings::wasi::io::poll::HostPollable for IoImpl
where
T: IoView,
{
@@ -206,38 +207,3 @@ where
Ok(())
}
}
-
-pub mod sync {
- use crate::{
- bindings::io::poll as async_poll,
- bindings::sync::io::poll::{self, Pollable},
- runtime::in_tokio,
- IoImpl, IoView,
- };
- use anyhow::Result;
- use wasmtime::component::Resource;
-
- impl poll::Host for IoImpl
- where
- T: IoView,
- {
- fn poll(&mut self, pollables: Vec>) -> Result> {
- in_tokio(async { async_poll::Host::poll(self, pollables).await })
- }
- }
-
- impl crate::bindings::sync::io::poll::HostPollable for IoImpl
- where
- T: IoView,
- {
- fn ready(&mut self, pollable: Resource) -> Result {
- in_tokio(async { async_poll::HostPollable::ready(self, pollable).await })
- }
- fn block(&mut self, pollable: Resource) -> Result<()> {
- in_tokio(async { async_poll::HostPollable::block(self, pollable).await })
- }
- fn drop(&mut self, pollable: Resource) -> Result<()> {
- async_poll::HostPollable::drop(self, pollable)
- }
- }
-}
diff --git a/crates/wasi/src/stream.rs b/crates/wasi-io/src/stream.rs
similarity index 100%
rename from crates/wasi/src/stream.rs
rename to crates/wasi-io/src/stream.rs
diff --git a/crates/wasi-io/src/view.rs b/crates/wasi-io/src/view.rs
new file mode 100644
index 000000000000..f46c8793fc3b
--- /dev/null
+++ b/crates/wasi-io/src/view.rs
@@ -0,0 +1,40 @@
+use wasmtime::component::ResourceTable;
+
+pub trait IoView: Send {
+ /// Yields mutable access to the internal resource management that this
+ /// context contains.
+ ///
+ /// Embedders can add custom resources to this table as well to give
+ /// resources to wasm as well.
+ fn table(&mut self) -> &mut ResourceTable;
+}
+impl IoView for &mut T {
+ fn table(&mut self) -> &mut ResourceTable {
+ T::table(self)
+ }
+}
+impl IoView for Box {
+ fn table(&mut self) -> &mut ResourceTable {
+ T::table(self)
+ }
+}
+
+/// A small newtype wrapper which serves as the basis for implementations of
+/// `Host` WASI traits in this crate.
+///
+/// This type is used as the basis for the implementation of all `Host` traits
+/// generated by `bindgen!` for WASI interfaces. This is used automatically with
+/// [`add_to_linker_sync`](crate::add_to_linker_sync) and
+/// [`add_to_linker_async`](crate::add_to_linker_async).
+///
+/// This type is otherwise provided if you're calling the `add_to_linker`
+/// functions generated by `bindgen!` from the [`bindings`
+/// module](crate::bindings). In this situation you'll want to create a value of
+/// this type in the closures added to a `Linker`.
+#[repr(transparent)]
+pub struct IoImpl(pub T);
+impl IoView for IoImpl {
+ fn table(&mut self) -> &mut ResourceTable {
+ T::table(&mut self.0)
+ }
+}
diff --git a/crates/wasi-io/wit/deps/io/error.wit b/crates/wasi-io/wit/deps/io/error.wit
new file mode 100644
index 000000000000..97c6068779ac
--- /dev/null
+++ b/crates/wasi-io/wit/deps/io/error.wit
@@ -0,0 +1,34 @@
+package wasi:io@0.2.3;
+
+@since(version = 0.2.0)
+interface error {
+ /// A resource which represents some error information.
+ ///
+ /// The only method provided by this resource is `to-debug-string`,
+ /// which provides some human-readable information about the error.
+ ///
+ /// In the `wasi:io` package, this resource is returned through the
+ /// `wasi:io/streams/stream-error` type.
+ ///
+ /// To provide more specific error information, other interfaces may
+ /// offer functions to "downcast" this error into more specific types. For example,
+ /// errors returned from streams derived from filesystem types can be described using
+ /// the filesystem's own error-code type. This is done using the function
+ /// `wasi:filesystem/types/filesystem-error-code`, which takes a `borrow`
+ /// parameter and returns an `option`.
+ ///
+ /// The set of functions which can "downcast" an `error` into a more
+ /// concrete type is open.
+ @since(version = 0.2.0)
+ resource error {
+ /// Returns a string that is suitable to assist humans in debugging
+ /// this error.
+ ///
+ /// WARNING: The returned string should not be consumed mechanically!
+ /// It may change across platforms, hosts, or other implementation
+ /// details. Parsing this string is a major platform-compatibility
+ /// hazard.
+ @since(version = 0.2.0)
+ to-debug-string: func() -> string;
+ }
+}
diff --git a/crates/wasi-io/wit/deps/io/poll.wit b/crates/wasi-io/wit/deps/io/poll.wit
new file mode 100644
index 000000000000..9bcbe8e03692
--- /dev/null
+++ b/crates/wasi-io/wit/deps/io/poll.wit
@@ -0,0 +1,47 @@
+package wasi:io@0.2.3;
+
+/// A poll API intended to let users wait for I/O events on multiple handles
+/// at once.
+@since(version = 0.2.0)
+interface poll {
+ /// `pollable` represents a single I/O event which may be ready, or not.
+ @since(version = 0.2.0)
+ resource pollable {
+
+ /// Return the readiness of a pollable. This function never blocks.
+ ///
+ /// Returns `true` when the pollable is ready, and `false` otherwise.
+ @since(version = 0.2.0)
+ ready: func() -> bool;
+
+ /// `block` returns immediately if the pollable is ready, and otherwise
+ /// blocks until ready.
+ ///
+ /// This function is equivalent to calling `poll.poll` on a list
+ /// containing only this pollable.
+ @since(version = 0.2.0)
+ block: func();
+ }
+
+ /// 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.
+ ///
+ /// This function traps if either:
+ /// - the list is empty, or:
+ /// - the list contains more elements than can be indexed with a `u32` value.
+ ///
+ /// 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 ready for I/O.
+ @since(version = 0.2.0)
+ poll: func(in: list>) -> list;
+}
diff --git a/crates/wasi-io/wit/deps/io/streams.wit b/crates/wasi-io/wit/deps/io/streams.wit
new file mode 100644
index 000000000000..0de0846293ff
--- /dev/null
+++ b/crates/wasi-io/wit/deps/io/streams.wit
@@ -0,0 +1,290 @@
+package wasi:io@0.2.3;
+
+/// WASI I/O is an I/O abstraction API which is currently focused on providing
+/// stream types.
+///
+/// In the future, the component model is expected to add built-in stream types;
+/// when it does, they are expected to subsume this API.
+@since(version = 0.2.0)
+interface streams {
+ @since(version = 0.2.0)
+ use error.{error};
+ @since(version = 0.2.0)
+ use poll.{pollable};
+
+ /// An error for input-stream and output-stream operations.
+ @since(version = 0.2.0)
+ variant stream-error {
+ /// The last operation (a write or flush) failed before completion.
+ ///
+ /// More information is available in the `error` payload.
+ ///
+ /// After this, the stream will be closed. All future operations return
+ /// `stream-error::closed`.
+ last-operation-failed(error),
+ /// The stream is closed: no more input will be accepted by the
+ /// stream. A closed output-stream will return this error on all
+ /// future operations.
+ closed
+ }
+
+ /// 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` function to obtain a `pollable` which can be polled
+ /// for using `wasi:io/poll`.
+ @since(version = 0.2.0)
+ resource input-stream {
+ /// Perform a non-blocking read from the stream.
+ ///
+ /// When the source of a `read` is binary data, the bytes from the source
+ /// are returned verbatim. When the source of a `read` is known to the
+ /// implementation to be text, bytes containing the UTF-8 encoding of the
+ /// text are returned.
+ ///
+ /// This function returns a list of bytes containing the read data,
+ /// when successful. The returned list will contain up to `len` bytes;
+ /// it may return fewer than requested, but not more. The list is
+ /// empty when no bytes are available for reading at this time. The
+ /// pollable given by `subscribe` will be ready when more bytes are
+ /// available.
+ ///
+ /// This function fails with a `stream-error` when the operation
+ /// encounters an error, giving `last-operation-failed`, or when the
+ /// stream is closed, giving `closed`.
+ ///
+ /// When the caller gives a `len` of 0, it represents a request to
+ /// read 0 bytes. If the stream is still open, this call should
+ /// succeed and return an empty list, or otherwise fail with `closed`.
+ ///
+ /// 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.
+ @since(version = 0.2.0)
+ read: func(
+ /// The maximum number of bytes to read
+ len: u64
+ ) -> result, stream-error>;
+
+ /// Read bytes from a stream, after blocking until at least one byte can
+ /// be read. Except for blocking, behavior is identical to `read`.
+ @since(version = 0.2.0)
+ blocking-read: func(
+ /// The maximum number of bytes to read
+ len: u64
+ ) -> result, stream-error>;
+
+ /// Skip bytes from a stream. Returns number of bytes skipped.
+ ///
+ /// Behaves identical to `read`, except instead of returning a list
+ /// of bytes, returns the number of bytes consumed from the stream.
+ @since(version = 0.2.0)
+ skip: func(
+ /// The maximum number of bytes to skip.
+ len: u64,
+ ) -> result;
+
+ /// Skip bytes from a stream, after blocking until at least one byte
+ /// can be skipped. Except for blocking behavior, identical to `skip`.
+ @since(version = 0.2.0)
+ blocking-skip: func(
+ /// The maximum number of bytes to skip.
+ len: u64,
+ ) -> result;
+
+ /// 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.
+ @since(version = 0.2.0)
+ subscribe: func() -> pollable;
+ }
+
+
+ /// An output bytestream.
+ ///
+ /// `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`.
+ ///
+ /// Dropping an `output-stream` while there's still an active write in
+ /// progress may result in the data being lost. Before dropping the stream,
+ /// be sure to fully flush your writes.
+ @since(version = 0.2.0)
+ 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.
+ @since(version = 0.2.0)
+ check-write: func() -> result;
+
+ /// Perform a write. This function never blocks.
+ ///
+ /// When the destination of a `write` is binary data, the bytes from
+ /// `contents` are written verbatim. When the destination of a `write` is
+ /// known to the implementation to be text, the bytes of `contents` are
+ /// transcoded from UTF-8 into the encoding of the destination and then
+ /// written.
+ ///
+ /// 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.
+ @since(version = 0.2.0)
+ write: func(
+ contents: list
+ ) -> result<_, stream-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
+ /// pollable.block();
+ /// 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`
+ /// pollable.block();
+ /// // Check for any errors that arose during `flush`
+ /// let _ = this.check-write(); // eliding error handling
+ /// ```
+ @since(version = 0.2.0)
+ blocking-write-and-flush: func(
+ contents: list
+ ) -> result<_, stream-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.
+ @since(version = 0.2.0)
+ flush: func() -> result<_, stream-error>;
+
+ /// Request to flush buffered output, and block until flush completes
+ /// and stream is ready for writing again.
+ @since(version = 0.2.0)
+ blocking-flush: func() -> result<_, stream-error>;
+
+ /// Create a `pollable` which will resolve once the output-stream
+ /// is ready for more writing, or an error has occurred. 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.
+ @since(version = 0.2.0)
+ subscribe: func() -> pollable;
+
+ /// 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.
+ @since(version = 0.2.0)
+ write-zeroes: func(
+ /// The number of zero-bytes to write
+ len: u64
+ ) -> result<_, stream-error>;
+
+ /// 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
+ /// pollable.block();
+ /// 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`
+ /// pollable.block();
+ /// // Check for any errors that arose during `flush`
+ /// let _ = this.check-write(); // eliding error handling
+ /// ```
+ @since(version = 0.2.0)
+ blocking-write-zeroes-and-flush: func(
+ /// The number of zero-bytes to write
+ len: u64
+ ) -> result<_, stream-error>;
+
+ /// Read from one stream and write to another.
+ ///
+ /// The behavior of splice is equivalent to:
+ /// 1. calling `check-write` on the `output-stream`
+ /// 2. calling `read` on the `input-stream` with the smaller of the
+ /// `check-write` permitted length and the `len` provided to `splice`
+ /// 3. calling `write` on the `output-stream` with that read data.
+ ///
+ /// Any error reported by the call to `check-write`, `read`, or
+ /// `write` ends the splice and reports that error.
+ ///
+ /// This function returns the number of bytes transferred; it may be less
+ /// than `len`.
+ @since(version = 0.2.0)
+ splice: func(
+ /// The stream to read from
+ src: borrow,
+ /// 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 the
+ /// `output-stream` is ready for writing, and the `input-stream`
+ /// is ready for reading, before performing the `splice`.
+ @since(version = 0.2.0)
+ blocking-splice: func(
+ /// The stream to read from
+ src: borrow,
+ /// The number of bytes to splice
+ len: u64,
+ ) -> result;
+ }
+}
diff --git a/crates/wasi-io/wit/deps/io/world.wit b/crates/wasi-io/wit/deps/io/world.wit
new file mode 100644
index 000000000000..f1d2102dca1d
--- /dev/null
+++ b/crates/wasi-io/wit/deps/io/world.wit
@@ -0,0 +1,10 @@
+package wasi:io@0.2.3;
+
+@since(version = 0.2.0)
+world imports {
+ @since(version = 0.2.0)
+ import streams;
+
+ @since(version = 0.2.0)
+ import poll;
+}
diff --git a/crates/wasi-io/wit/world.wit b/crates/wasi-io/wit/world.wit
new file mode 100644
index 000000000000..258bac64c4fa
--- /dev/null
+++ b/crates/wasi-io/wit/world.wit
@@ -0,0 +1,6 @@
+// We actually don't use this; it's just to let bindgen! find the corresponding world in wit/deps.
+package wasmtime:wasi-io;
+
+world bindings {
+ include wasi:io/imports@0.2.3;
+}
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 58375b784f70..fc5603de02d3 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -1,5 +1,6 @@
//! # Wasmtime's WASI Implementation
//!
+//!
//! This crate provides a Wasmtime host implementation of WASI 0.2 (aka WASIp2
//! aka Preview 2) and WASI 0.1 (aka WASIp1 aka Preview 1). WASI is implemented
//! with the Rust crates [`tokio`] and [`cap-std`] primarily, meaning that
@@ -202,7 +203,6 @@ pub mod preview1;
mod random;
pub mod runtime;
mod stdio;
-mod stream;
mod tcp;
mod udp;
mod view;
@@ -213,15 +213,11 @@ pub use self::ctx::{WasiCtx, WasiCtxBuilder};
pub use self::error::{I32Exit, TrappableError};
pub use self::filesystem::{DirPerms, FileInputStream, FilePerms, FsError, FsResult};
pub use self::network::{Network, SocketAddrUse, SocketError, SocketResult};
-pub use self::poll::{subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe};
pub use self::random::{thread_rng, Deterministic};
pub use self::stdio::{
stderr, stdin, stdout, AsyncStdinStream, AsyncStdoutStream, IsATTY, OutputFile, Stderr, Stdin,
StdinStream, Stdout, StdoutStream,
};
-pub use self::stream::{
- HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
-};
pub use self::view::{IoImpl, IoView, WasiImpl, WasiView};
#[doc(no_inline)]
pub use async_trait::async_trait;
@@ -231,6 +227,12 @@ pub use cap_fs_ext::SystemTimeSpec;
pub use cap_rand::RngCore;
#[doc(no_inline)]
pub use wasmtime::component::{ResourceTable, ResourceTableError};
+pub use wasmtime_wasi_io::poll::{
+ subscribe, ClosureFuture, MakeFuture, Pollable, PollableFuture, Subscribe,
+};
+pub use wasmtime_wasi_io::stream::{
+ HostInputStream, HostOutputStream, InputStream, OutputStream, StreamError, StreamResult,
+};
/// Add all WASI interfaces from this crate into the `linker` provided.
///
diff --git a/crates/wasi/src/view.rs b/crates/wasi/src/view.rs
index 58375b1b60ab..3a39c73dc360 100644
--- a/crates/wasi/src/view.rs
+++ b/crates/wasi/src/view.rs
@@ -1,14 +1,5 @@
use crate::ctx::WasiCtx;
-use wasmtime::component::ResourceTable;
-
-pub trait IoView: Send {
- /// Yields mutable access to the internal resource management that this
- /// context contains.
- ///
- /// Embedders can add custom resources to this table as well to give
- /// resources to wasm as well.
- fn table(&mut self) -> &mut ResourceTable;
-}
+pub use wasmtime_wasi_io::{IoImpl, IoView};
pub trait WasiView: IoView {
/// Yields mutable access to the configuration used for this context.
@@ -17,48 +8,18 @@ pub trait WasiView: IoView {
fn ctx(&mut self) -> &mut WasiCtx;
}
-impl IoView for &mut T {
- fn table(&mut self) -> &mut ResourceTable {
- T::table(self)
- }
-}
impl WasiView for &mut T {
fn ctx(&mut self) -> &mut WasiCtx {
T::ctx(self)
}
}
-impl IoView for Box {
- fn table(&mut self) -> &mut ResourceTable {
- T::table(self)
- }
-}
impl WasiView for Box {
fn ctx(&mut self) -> &mut WasiCtx {
T::ctx(self)
}
}
-/// A small newtype wrapper which serves as the basis for implementations of
-/// `Host` WASI traits in this crate.
-///
-/// This type is used as the basis for the implementation of all `Host` traits
-/// generated by `bindgen!` for WASI interfaces. This is used automatically with
-/// [`add_to_linker_sync`](crate::add_to_linker_sync) and
-/// [`add_to_linker_async`](crate::add_to_linker_async).
-///
-/// This type is otherwise provided if you're calling the `add_to_linker`
-/// functions generated by `bindgen!` from the [`bindings`
-/// module](crate::bindings). In this situation you'll want to create a value of
-/// this type in the closures added to a `Linker`.
-#[repr(transparent)]
-pub struct IoImpl(pub T);
-impl IoView for IoImpl {
- fn table(&mut self) -> &mut ResourceTable {
- T::table(&mut self.0)
- }
-}
-
/// A small newtype wrapper which serves as the basis for implementations of
/// `Host` WASI traits in this crate.
///
From 0fb213156dd1e821ccc510a5d2a16048f8e48fae Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 16:40:56 -0800
Subject: [PATCH 04/27] stump of poll that uses in_tokio
---
crates/wasi/src/poll.rs | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 crates/wasi/src/poll.rs
diff --git a/crates/wasi/src/poll.rs b/crates/wasi/src/poll.rs
new file mode 100644
index 000000000000..de80269434f0
--- /dev/null
+++ b/crates/wasi/src/poll.rs
@@ -0,0 +1,29 @@
+pub mod sync {
+ use crate::{bindings::io::poll as async_poll, runtime::in_tokio, IoImpl, IoView};
+ use anyhow::Result;
+ use wasmtime::component::Resource;
+
+ impl poll::Host for IoImpl
+ where
+ T: IoView,
+ {
+ fn poll(&mut self, pollables: Vec>) -> Result> {
+ in_tokio(async { async_poll::Host::poll(self, pollables).await })
+ }
+ }
+
+ impl crate::bindings::sync::io::poll::HostPollable for IoImpl
+ where
+ T: IoView,
+ {
+ fn ready(&mut self, pollable: Resource) -> Result {
+ in_tokio(async { async_poll::HostPollable::ready(self, pollable).await })
+ }
+ fn block(&mut self, pollable: Resource) -> Result<()> {
+ in_tokio(async { async_poll::HostPollable::block(self, pollable).await })
+ }
+ fn drop(&mut self, pollable: Resource) -> Result<()> {
+ async_poll::HostPollable::drop(self, pollable)
+ }
+ }
+}
From cb5e14ba0f3a2b2120067aac2139a21fece645c0 Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 17:12:20 -0800
Subject: [PATCH 05/27] finish moving instances over to wasmtime_wasi_io
---
crates/wasi-io/src/io.rs | 217 ++++++++++++
crates/wasi-io/src/lib.rs | 2 +
crates/wasi/src/bindings.rs | 21 +-
crates/wasi/src/host/clocks.rs | 2 +-
crates/wasi/src/host/io.rs | 344 +++----------------
crates/wasi/src/host/tcp.rs | 5 +-
crates/wasi/src/host/udp.rs | 9 +-
crates/wasi/src/ip_name_lookup.rs | 2 +-
crates/wasi/src/lib.rs | 15 +-
crates/wasi/src/pipe.rs | 6 +-
crates/wasi/src/poll.rs | 48 +--
crates/wasi/src/preview1.rs | 13 +-
crates/wasi/src/stdio/worker_thread_stdin.rs | 6 +-
crates/wasi/src/udp.rs | 2 +-
crates/wasi/src/view.rs | 1 +
15 files changed, 344 insertions(+), 349 deletions(-)
create mode 100644 crates/wasi-io/src/io.rs
diff --git a/crates/wasi-io/src/io.rs b/crates/wasi-io/src/io.rs
new file mode 100644
index 000000000000..804728a99104
--- /dev/null
+++ b/crates/wasi-io/src/io.rs
@@ -0,0 +1,217 @@
+use crate::bindings::wasi::io::{error, streams};
+use crate::poll::{subscribe, Pollable};
+use crate::stream::{InputStream, OutputStream, StreamError, StreamResult};
+use crate::view::{IoImpl, IoView};
+use wasmtime::component::Resource;
+
+impl error::Host for IoImpl where T: IoView {}
+
+impl streams::Host for IoImpl
+where
+ T: IoView,
+{
+ fn convert_stream_error(&mut self, err: StreamError) -> anyhow::Result {
+ match err {
+ StreamError::Closed => Ok(streams::StreamError::Closed),
+ StreamError::LastOperationFailed(e) => Ok(streams::StreamError::LastOperationFailed(
+ self.table().push(e)?,
+ )),
+ StreamError::Trap(e) => Err(e),
+ }
+ }
+}
+
+impl error::HostError for IoImpl
+where
+ T: IoView,
+{
+ fn drop(&mut self, err: Resource) -> anyhow::Result<()> {
+ self.table().delete(err)?;
+ Ok(())
+ }
+
+ fn to_debug_string(&mut self, err: Resource) -> anyhow::Result {
+ Ok(format!("{:?}", self.table().get(&err)?))
+ }
+}
+
+impl streams::HostOutputStream for IoImpl
+where
+ T: IoView,
+{
+ async fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
+ self.table().delete(stream)?.cancel().await;
+ Ok(())
+ }
+
+ fn check_write(&mut self, stream: Resource) -> StreamResult {
+ let bytes = self.table().get_mut(&stream)?.check_write()?;
+ Ok(bytes as u64)
+ }
+
+ fn write(&mut self, stream: Resource, bytes: Vec) -> StreamResult<()> {
+ self.table().get_mut(&stream)?.write(bytes.into())?;
+ Ok(())
+ }
+
+ fn subscribe(&mut self, stream: Resource) -> anyhow::Result> {
+ subscribe(self.table(), stream)
+ }
+
+ async fn blocking_write_and_flush(
+ &mut self,
+ stream: Resource,
+ bytes: Vec,
+ ) -> StreamResult<()> {
+ if bytes.len() > 4096 {
+ return Err(StreamError::trap(
+ "Buffer too large for blocking-write-and-flush (expected at most 4096)",
+ ));
+ }
+
+ self.table()
+ .get_mut(&stream)?
+ .blocking_write_and_flush(bytes.into())
+ .await
+ }
+
+ async fn blocking_write_zeroes_and_flush(
+ &mut self,
+ stream: Resource,
+ len: u64,
+ ) -> StreamResult<()> {
+ if len > 4096 {
+ return Err(StreamError::trap(
+ "Buffer too large for blocking-write-zeroes-and-flush (expected at most 4096)",
+ ));
+ }
+
+ self.table()
+ .get_mut(&stream)?
+ .blocking_write_zeroes_and_flush(len as usize)
+ .await
+ }
+
+ fn write_zeroes(&mut self, stream: Resource, len: u64) -> StreamResult<()> {
+ self.table().get_mut(&stream)?.write_zeroes(len as usize)?;
+ Ok(())
+ }
+
+ fn flush(&mut self, stream: Resource) -> StreamResult<()> {
+ self.table().get_mut(&stream)?.flush()?;
+ Ok(())
+ }
+
+ async fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> {
+ let s = self.table().get_mut(&stream)?;
+ s.flush()?;
+ s.write_ready().await?;
+ Ok(())
+ }
+
+ fn splice(
+ &mut self,
+ dest: Resource,
+ src: Resource,
+ len: u64,
+ ) -> StreamResult {
+ let len = len.try_into().unwrap_or(usize::MAX);
+
+ let permit = {
+ let output = self.table().get_mut(&dest)?;
+ output.check_write()?
+ };
+ let len = len.min(permit);
+ if len == 0 {
+ return Ok(0);
+ }
+
+ let contents = self.table().get_mut(&src)?.read(len)?;
+
+ let len = contents.len();
+ if len == 0 {
+ return Ok(0);
+ }
+
+ let output = self.table().get_mut(&dest)?;
+ output.write(contents)?;
+ Ok(len.try_into().expect("usize can fit in u64"))
+ }
+
+ async fn blocking_splice(
+ &mut self,
+ dest: Resource,
+ src: Resource,
+ len: u64,
+ ) -> StreamResult {
+ let len = len.try_into().unwrap_or(usize::MAX);
+
+ let permit = {
+ let output = self.table().get_mut(&dest)?;
+ output.write_ready().await?
+ };
+ let len = len.min(permit);
+ if len == 0 {
+ return Ok(0);
+ }
+
+ let contents = self.table().get_mut(&src)?.blocking_read(len).await?;
+
+ let len = contents.len();
+ if len == 0 {
+ return Ok(0);
+ }
+
+ let output = self.table().get_mut(&dest)?;
+ output.blocking_write_and_flush(contents).await?;
+ Ok(len.try_into().expect("usize can fit in u64"))
+ }
+}
+
+impl streams::HostInputStream for IoImpl
+where
+ T: IoView,
+{
+ async fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
+ self.table().delete(stream)?.cancel().await;
+ Ok(())
+ }
+
+ fn read(&mut self, stream: Resource, len: u64) -> StreamResult> {
+ let len = len.try_into().unwrap_or(usize::MAX);
+ let bytes = self.table().get_mut(&stream)?.read(len)?;
+ debug_assert!(bytes.len() <= len);
+ Ok(bytes.into())
+ }
+
+ async fn blocking_read(
+ &mut self,
+ stream: Resource,
+ len: u64,
+ ) -> StreamResult> {
+ let len = len.try_into().unwrap_or(usize::MAX);
+ let bytes = self.table().get_mut(&stream)?.blocking_read(len).await?;
+ debug_assert!(bytes.len() <= len);
+ Ok(bytes.into())
+ }
+
+ fn skip(&mut self, stream: Resource, len: u64) -> StreamResult {
+ let len = len.try_into().unwrap_or(usize::MAX);
+ let written = self.table().get_mut(&stream)?.skip(len)?;
+ Ok(written.try_into().expect("usize always fits in u64"))
+ }
+
+ async fn blocking_skip(
+ &mut self,
+ stream: Resource,
+ len: u64,
+ ) -> StreamResult {
+ let len = len.try_into().unwrap_or(usize::MAX);
+ let written = self.table().get_mut(&stream)?.blocking_skip(len).await?;
+ Ok(written.try_into().expect("usize always fits in u64"))
+ }
+
+ fn subscribe(&mut self, stream: Resource) -> anyhow::Result> {
+ crate::poll::subscribe(self.table(), stream)
+ }
+}
diff --git a/crates/wasi-io/src/lib.rs b/crates/wasi-io/src/lib.rs
index cbb457f90548..01c2d31d826d 100644
--- a/crates/wasi-io/src/lib.rs
+++ b/crates/wasi-io/src/lib.rs
@@ -1,3 +1,4 @@
+mod io;
pub mod poll;
pub mod stream;
mod view;
@@ -12,6 +13,7 @@ pub mod bindings {
"wasi:io/poll/pollable": crate::poll::Pollable,
"wasi:io/streams/input-stream": crate::stream::InputStream,
"wasi:io/streams/output-stream": crate::stream::OutputStream,
+ "wasi:io/error/error": crate::stream::Error,
},
async: {
only_imports: [
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
index 7449b3a3f202..72d7941c151f 100644
--- a/crates/wasi/src/bindings.rs
+++ b/crates/wasi/src/bindings.rs
@@ -146,7 +146,8 @@
/// ```
pub mod sync {
mod generated {
- use crate::{FsError, SocketError, StreamError};
+ use crate::{FsError, SocketError};
+ use wasmtime_wasi_io::stream::StreamError;
wasmtime::component::bindgen!({
path: "wit",
@@ -164,7 +165,7 @@ pub mod sync {
"wasi:clocks": crate::bindings::clocks,
"wasi:random": crate::bindings::random,
"wasi:cli": crate::bindings::cli,
- "wasi:io/error": crate::bindings::io::error,
+ "wasi:io/error": ::wasmtime_wasi_io::bindings::wasi::io::error,
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
"wasi:sockets/network": crate::bindings::sockets::network,
@@ -173,9 +174,9 @@ pub mod sync {
// way everything has the same type.
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
- "wasi:io/poll/pollable": super::super::io::poll::Pollable,
- "wasi:io/streams/input-stream": super::super::io::streams::InputStream,
- "wasi:io/streams/output-stream": super::super::io::streams::OutputStream,
+ "wasi:io/poll/pollable": wasmtime_wasi_io::poll::Pollable,
+ "wasi:io/streams/input-stream": wasmtime_wasi_io::stream::InputStream,
+ "wasi:io/streams/output-stream": wasmtime_wasi_io::stream::OutputStream,
"wasi:sockets/tcp/tcp-socket": super::super::sockets::tcp::TcpSocket,
"wasi:sockets/udp/incoming-datagram-stream": super::super::sockets::udp::IncomingDatagramStream,
"wasi:sockets/udp/outgoing-datagram-stream": super::super::sockets::udp::OutgoingDatagramStream,
@@ -399,7 +400,7 @@ mod async_io {
],
},
trappable_error_type: {
- "wasi:io/streams/stream-error" => crate::StreamError,
+ "wasi:io/streams/stream-error" => wasmtime_wasi_io::stream::StreamError,
"wasi:filesystem/types/error-code" => crate::FsError,
"wasi:sockets/network/error-code" => crate::SocketError,
},
@@ -415,10 +416,10 @@ mod async_io {
"wasi:sockets/ip-name-lookup/resolve-address-stream": crate::ip_name_lookup::ResolveAddressStream,
"wasi:filesystem/types/directory-entry-stream": crate::filesystem::ReaddirIterator,
"wasi:filesystem/types/descriptor": crate::filesystem::Descriptor,
- "wasi:io/streams/input-stream": crate::stream::InputStream,
- "wasi:io/streams/output-stream": crate::stream::OutputStream,
- "wasi:io/error/error": crate::stream::Error,
- "wasi:io/poll/pollable": crate::poll::Pollable,
+ "wasi:io/streams/input-stream": wasmtime_wasi_io::stream::InputStream,
+ "wasi:io/streams/output-stream": wasmtime_wasi_io::stream::OutputStream,
+ "wasi:io/error/error": wasmtime_wasi_io::stream::Error,
+ "wasi:io/poll/pollable": wasmtime_wasi_io::poll::Pollable,
"wasi:cli/terminal-input/terminal-input": crate::stdio::TerminalInput,
"wasi:cli/terminal-output/terminal-output": crate::stdio::TerminalOutput,
},
diff --git a/crates/wasi/src/host/clocks.rs b/crates/wasi/src/host/clocks.rs
index feefb9473b27..f81a6a255ab3 100644
--- a/crates/wasi/src/host/clocks.rs
+++ b/crates/wasi/src/host/clocks.rs
@@ -4,11 +4,11 @@ use crate::bindings::{
clocks::monotonic_clock::{self, Duration as WasiDuration, Instant},
clocks::wall_clock::{self, Datetime},
};
-use crate::poll::{subscribe, Subscribe};
use crate::{IoView, Pollable, WasiImpl, WasiView};
use cap_std::time::SystemTime;
use std::time::Duration;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::poll::{subscribe, Subscribe};
impl TryFrom for Datetime {
type Error = anyhow::Error;
diff --git a/crates/wasi/src/host/io.rs b/crates/wasi/src/host/io.rs
index 5ad1a65e84da..75d41865102e 100644
--- a/crates/wasi/src/host/io.rs
+++ b/crates/wasi/src/host/io.rs
@@ -1,39 +1,30 @@
use crate::{
- bindings::io::error,
- bindings::io::streams::{self, InputStream, OutputStream},
- poll::subscribe,
- IoImpl, IoView, Pollable, StreamError, StreamResult,
+ bindings::sync::io::poll::Pollable,
+ bindings::sync::io::streams::{self, InputStream, OutputStream},
+ runtime::in_tokio,
+ IoImpl, IoView, StreamError, StreamResult,
};
use wasmtime::component::Resource;
+use wasmtime_wasi_io::bindings::wasi::io::streams::{
+ self as async_streams, Host as AsyncHost, HostInputStream as AsyncHostInputStream,
+ HostOutputStream as AsyncHostOutputStream,
+};
-impl error::Host for IoImpl where T: IoView {}
-
-impl streams::Host for IoImpl
-where
- T: IoView,
-{
- fn convert_stream_error(&mut self, err: StreamError) -> anyhow::Result {
- match err {
- StreamError::Closed => Ok(streams::StreamError::Closed),
- StreamError::LastOperationFailed(e) => Ok(streams::StreamError::LastOperationFailed(
- self.table().push(e)?,
- )),
- StreamError::Trap(e) => Err(e),
+impl From for streams::StreamError {
+ fn from(other: async_streams::StreamError) -> Self {
+ match other {
+ async_streams::StreamError::LastOperationFailed(e) => Self::LastOperationFailed(e),
+ async_streams::StreamError::Closed => Self::Closed,
}
}
}
-impl error::HostError for IoImpl
+impl streams::Host for IoImpl
where
T: IoView,
{
- fn drop(&mut self, err: Resource) -> anyhow::Result<()> {
- self.table().delete(err)?;
- Ok(())
- }
-
- fn to_debug_string(&mut self, err: Resource) -> anyhow::Result {
- Ok(format!("{:?}", self.table().get(&err)?))
+ fn convert_stream_error(&mut self, err: StreamError) -> anyhow::Result {
+ Ok(AsyncHost::convert_stream_error(self, err)?.into())
}
}
@@ -41,132 +32,75 @@ impl streams::HostOutputStream for IoImpl
where
T: IoView,
{
- async fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
- self.table().delete(stream)?.cancel().await;
- Ok(())
+ fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
+ in_tokio(async { AsyncHostOutputStream::drop(self, stream).await })
}
fn check_write(&mut self, stream: Resource) -> StreamResult {
- let bytes = self.table().get_mut(&stream)?.check_write()?;
- Ok(bytes as u64)
+ Ok(AsyncHostOutputStream::check_write(self, stream)?)
}
fn write(&mut self, stream: Resource, bytes: Vec) -> StreamResult<()> {
- self.table().get_mut(&stream)?.write(bytes.into())?;
- Ok(())
+ Ok(AsyncHostOutputStream::write(self, stream, bytes)?)
}
- fn subscribe(&mut self, stream: Resource) -> anyhow::Result> {
- subscribe(self.table(), stream)
- }
-
- async fn blocking_write_and_flush(
+ fn blocking_write_and_flush(
&mut self,
stream: Resource,
bytes: Vec,
) -> StreamResult<()> {
- if bytes.len() > 4096 {
- return Err(StreamError::trap(
- "Buffer too large for blocking-write-and-flush (expected at most 4096)",
- ));
- }
-
- self.table()
- .get_mut(&stream)?
- .blocking_write_and_flush(bytes.into())
- .await
+ in_tokio(async {
+ AsyncHostOutputStream::blocking_write_and_flush(self, stream, bytes).await
+ })
}
- async fn blocking_write_zeroes_and_flush(
+ fn blocking_write_zeroes_and_flush(
&mut self,
stream: Resource,
len: u64,
) -> StreamResult<()> {
- if len > 4096 {
- return Err(StreamError::trap(
- "Buffer too large for blocking-write-zeroes-and-flush (expected at most 4096)",
- ));
- }
+ in_tokio(async {
+ AsyncHostOutputStream::blocking_write_zeroes_and_flush(self, stream, len).await
+ })
+ }
- self.table()
- .get_mut(&stream)?
- .blocking_write_zeroes_and_flush(len as usize)
- .await
+ fn subscribe(&mut self, stream: Resource) -> anyhow::Result> {
+ Ok(AsyncHostOutputStream::subscribe(self, stream)?)
}
fn write_zeroes(&mut self, stream: Resource, len: u64) -> StreamResult<()> {
- self.table().get_mut(&stream)?.write_zeroes(len as usize)?;
- Ok(())
+ Ok(AsyncHostOutputStream::write_zeroes(self, stream, len)?)
}
fn flush(&mut self, stream: Resource) -> StreamResult<()> {
- self.table().get_mut(&stream)?.flush()?;
- Ok(())
+ Ok(AsyncHostOutputStream::flush(
+ self,
+ Resource::new_borrow(stream.rep()),
+ )?)
}
- async fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> {
- let s = self.table().get_mut(&stream)?;
- s.flush()?;
- s.write_ready().await?;
- Ok(())
+ fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> {
+ in_tokio(async {
+ AsyncHostOutputStream::blocking_flush(self, Resource::new_borrow(stream.rep())).await
+ })
}
fn splice(
&mut self,
- dest: Resource,
+ dst: Resource,
src: Resource,
len: u64,
) -> StreamResult {
- let len = len.try_into().unwrap_or(usize::MAX);
-
- let permit = {
- let output = self.table().get_mut(&dest)?;
- output.check_write()?
- };
- let len = len.min(permit);
- if len == 0 {
- return Ok(0);
- }
-
- let contents = self.table().get_mut(&src)?.read(len)?;
-
- let len = contents.len();
- if len == 0 {
- return Ok(0);
- }
-
- let output = self.table().get_mut(&dest)?;
- output.write(contents)?;
- Ok(len.try_into().expect("usize can fit in u64"))
+ AsyncHostOutputStream::splice(self, dst, src, len)
}
- async fn blocking_splice(
+ fn blocking_splice(
&mut self,
- dest: Resource,
+ dst: Resource,
src: Resource,
len: u64,
) -> StreamResult {
- let len = len.try_into().unwrap_or(usize::MAX);
-
- let permit = {
- let output = self.table().get_mut(&dest)?;
- output.write_ready().await?
- };
- let len = len.min(permit);
- if len == 0 {
- return Ok(0);
- }
-
- let contents = self.table().get_mut(&src)?.blocking_read(len).await?;
-
- let len = contents.len();
- if len == 0 {
- return Ok(0);
- }
-
- let output = self.table().get_mut(&dest)?;
- output.blocking_write_and_flush(contents).await?;
- Ok(len.try_into().expect("usize can fit in u64"))
+ in_tokio(async { AsyncHostOutputStream::blocking_splice(self, dst, src, len).await })
}
}
@@ -174,197 +108,27 @@ impl streams::HostInputStream for IoImpl
where
T: IoView,
{
- async fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
- self.table().delete(stream)?.cancel().await;
- Ok(())
+ fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
+ in_tokio(async { AsyncHostInputStream::drop(self, stream).await })
}
fn read(&mut self, stream: Resource, len: u64) -> StreamResult> {
- let len = len.try_into().unwrap_or(usize::MAX);
- let bytes = self.table().get_mut(&stream)?.read(len)?;
- debug_assert!(bytes.len() <= len);
- Ok(bytes.into())
+ AsyncHostInputStream::read(self, stream, len)
}
- async fn blocking_read(
- &mut self,
- stream: Resource,
- len: u64,
- ) -> StreamResult> {
- let len = len.try_into().unwrap_or(usize::MAX);
- let bytes = self.table().get_mut(&stream)?.blocking_read(len).await?;
- debug_assert!(bytes.len() <= len);
- Ok(bytes.into())
+ fn blocking_read(&mut self, stream: Resource, len: u64) -> StreamResult> {
+ in_tokio(async { AsyncHostInputStream::blocking_read(self, stream, len).await })
}
fn skip(&mut self, stream: Resource, len: u64) -> StreamResult {
- let len = len.try_into().unwrap_or(usize::MAX);
- let written = self.table().get_mut(&stream)?.skip(len)?;
- Ok(written.try_into().expect("usize always fits in u64"))
+ AsyncHostInputStream::skip(self, stream, len)
}
- async fn blocking_skip(
- &mut self,
- stream: Resource,
- len: u64,
- ) -> StreamResult {
- let len = len.try_into().unwrap_or(usize::MAX);
- let written = self.table().get_mut(&stream)?.blocking_skip(len).await?;
- Ok(written.try_into().expect("usize always fits in u64"))
+ fn blocking_skip(&mut self, stream: Resource, len: u64) -> StreamResult {
+ in_tokio(async { AsyncHostInputStream::blocking_skip(self, stream, len).await })
}
fn subscribe(&mut self, stream: Resource) -> anyhow::Result> {
- crate::poll::subscribe(self.table(), stream)
- }
-}
-
-pub mod sync {
- use crate::{
- bindings::io::streams::{
- self as async_streams, Host as AsyncHost, HostInputStream as AsyncHostInputStream,
- HostOutputStream as AsyncHostOutputStream,
- },
- bindings::sync::io::poll::Pollable,
- bindings::sync::io::streams::{self, InputStream, OutputStream},
- runtime::in_tokio,
- IoImpl, IoView, StreamError, StreamResult,
- };
- use wasmtime::component::Resource;
-
- impl From for streams::StreamError {
- fn from(other: async_streams::StreamError) -> Self {
- match other {
- async_streams::StreamError::LastOperationFailed(e) => Self::LastOperationFailed(e),
- async_streams::StreamError::Closed => Self::Closed,
- }
- }
- }
-
- impl streams::Host for IoImpl
- where
- T: IoView,
- {
- fn convert_stream_error(
- &mut self,
- err: StreamError,
- ) -> anyhow::Result {
- Ok(AsyncHost::convert_stream_error(self, err)?.into())
- }
- }
-
- impl streams::HostOutputStream for IoImpl
- where
- T: IoView,
- {
- fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
- in_tokio(async { AsyncHostOutputStream::drop(self, stream).await })
- }
-
- fn check_write(&mut self, stream: Resource) -> StreamResult {
- Ok(AsyncHostOutputStream::check_write(self, stream)?)
- }
-
- fn write(&mut self, stream: Resource, bytes: Vec) -> StreamResult<()> {
- Ok(AsyncHostOutputStream::write(self, stream, bytes)?)
- }
-
- fn blocking_write_and_flush(
- &mut self,
- stream: Resource,
- bytes: Vec,
- ) -> StreamResult<()> {
- in_tokio(async {
- AsyncHostOutputStream::blocking_write_and_flush(self, stream, bytes).await
- })
- }
-
- fn blocking_write_zeroes_and_flush(
- &mut self,
- stream: Resource,
- len: u64,
- ) -> StreamResult<()> {
- in_tokio(async {
- AsyncHostOutputStream::blocking_write_zeroes_and_flush(self, stream, len).await
- })
- }
-
- fn subscribe(
- &mut self,
- stream: Resource,
- ) -> anyhow::Result> {
- Ok(AsyncHostOutputStream::subscribe(self, stream)?)
- }
-
- fn write_zeroes(&mut self, stream: Resource, len: u64) -> StreamResult<()> {
- Ok(AsyncHostOutputStream::write_zeroes(self, stream, len)?)
- }
-
- fn flush(&mut self, stream: Resource) -> StreamResult<()> {
- Ok(AsyncHostOutputStream::flush(
- self,
- Resource::new_borrow(stream.rep()),
- )?)
- }
-
- fn blocking_flush(&mut self, stream: Resource) -> StreamResult<()> {
- in_tokio(async {
- AsyncHostOutputStream::blocking_flush(self, Resource::new_borrow(stream.rep()))
- .await
- })
- }
-
- fn splice(
- &mut self,
- dst: Resource,
- src: Resource,
- len: u64,
- ) -> StreamResult {
- AsyncHostOutputStream::splice(self, dst, src, len)
- }
-
- fn blocking_splice(
- &mut self,
- dst: Resource,
- src: Resource,
- len: u64,
- ) -> StreamResult {
- in_tokio(async { AsyncHostOutputStream::blocking_splice(self, dst, src, len).await })
- }
- }
-
- impl streams::HostInputStream for IoImpl
- where
- T: IoView,
- {
- fn drop(&mut self, stream: Resource) -> anyhow::Result<()> {
- in_tokio(async { AsyncHostInputStream::drop(self, stream).await })
- }
-
- fn read(&mut self, stream: Resource, len: u64) -> StreamResult> {
- AsyncHostInputStream::read(self, stream, len)
- }
-
- fn blocking_read(
- &mut self,
- stream: Resource,
- len: u64,
- ) -> StreamResult> {
- in_tokio(async { AsyncHostInputStream::blocking_read(self, stream, len).await })
- }
-
- fn skip(&mut self, stream: Resource, len: u64) -> StreamResult {
- AsyncHostInputStream::skip(self, stream, len)
- }
-
- fn blocking_skip(&mut self, stream: Resource, len: u64) -> StreamResult {
- in_tokio(async { AsyncHostInputStream::blocking_skip(self, stream, len).await })
- }
-
- fn subscribe(
- &mut self,
- stream: Resource,
- ) -> anyhow::Result> {
- AsyncHostInputStream::subscribe(self, stream)
- }
+ AsyncHostInputStream::subscribe(self, stream)
}
}
diff --git a/crates/wasi/src/host/tcp.rs b/crates/wasi/src/host/tcp.rs
index 04bfafd7141a..123c71ce1eb1 100644
--- a/crates/wasi/src/host/tcp.rs
+++ b/crates/wasi/src/host/tcp.rs
@@ -7,10 +7,11 @@ use crate::{
},
network::SocketAddressFamily,
};
-use crate::{IoView, Pollable, SocketResult, WasiImpl, WasiView};
+use crate::{SocketResult, WasiImpl, WasiView};
use std::net::SocketAddr;
use std::time::Duration;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::{poll::Pollable, IoView};
impl tcp::Host for WasiImpl where T: WasiView {}
@@ -281,7 +282,7 @@ where
}
fn subscribe(&mut self, this: Resource) -> anyhow::Result> {
- crate::poll::subscribe(self.table(), this)
+ wasmtime_wasi_io::poll::subscribe(self.table(), this)
}
fn shutdown(
diff --git a/crates/wasi/src/host/udp.rs b/crates/wasi/src/host/udp.rs
index 98f1a5644da6..fbd54e1b4762 100644
--- a/crates/wasi/src/host/udp.rs
+++ b/crates/wasi/src/host/udp.rs
@@ -8,7 +8,7 @@ use crate::{
udp::{IncomingDatagramStream, OutgoingDatagramStream, SendState, UdpState},
Subscribe,
};
-use crate::{IoView, Pollable, SocketError, SocketResult, WasiImpl, WasiView};
+use crate::{IoView, SocketError, SocketResult, WasiImpl, WasiView};
use anyhow::anyhow;
use async_trait::async_trait;
use io_lifetimes::AsSocketlike;
@@ -16,6 +16,7 @@ use rustix::io::Errno;
use std::net::SocketAddr;
use tokio::io::Interest;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::poll::Pollable;
/// Theoretical maximum byte size of a UDP datagram, the real limit is lower,
/// but we do not account for e.g. the transport layer here for simplicity.
@@ -288,7 +289,7 @@ where
}
fn subscribe(&mut self, this: Resource) -> anyhow::Result> {
- crate::poll::subscribe(self.table(), this)
+ wasmtime_wasi_io::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> {
@@ -371,7 +372,7 @@ where
&mut self,
this: Resource,
) -> anyhow::Result> {
- crate::poll::subscribe(self.table(), this)
+ wasmtime_wasi_io::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> {
@@ -510,7 +511,7 @@ where
&mut self,
this: Resource,
) -> anyhow::Result> {
- crate::poll::subscribe(self.table(), this)
+ wasmtime_wasi_io::poll::subscribe(self.table(), this)
}
fn drop(&mut self, this: Resource) -> Result<(), anyhow::Error> {
diff --git a/crates/wasi/src/ip_name_lookup.rs b/crates/wasi/src/ip_name_lookup.rs
index 8ed47b0d1e50..9c73e40b6f83 100644
--- a/crates/wasi/src/ip_name_lookup.rs
+++ b/crates/wasi/src/ip_name_lookup.rs
@@ -1,7 +1,6 @@
use crate::bindings::sockets::ip_name_lookup::{Host, HostResolveAddressStream};
use crate::bindings::sockets::network::{ErrorCode, IpAddress, Network};
use crate::host::network::util;
-use crate::poll::{subscribe, Pollable, Subscribe};
use crate::runtime::{spawn_blocking, AbortOnDropJoinHandle};
use crate::{IoView, SocketError, WasiImpl, WasiView};
use anyhow::Result;
@@ -11,6 +10,7 @@ use std::pin::Pin;
use std::str::FromStr;
use std::vec;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::poll::{subscribe, Pollable, Subscribe};
use super::network::{from_ipv4_addr, from_ipv6_addr};
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index fc5603de02d3..53f003e07df0 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -304,15 +304,16 @@ pub fn add_to_linker_with_options_async(
) -> anyhow::Result<()> {
let l = linker;
let io_closure = io_type_annotate::(|t| IoImpl(t));
+ wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker_get_host(l, io_closure)?;
+ wasmtime_wasi_io::bindings::wasi::io::poll::add_to_linker_get_host(l, io_closure)?;
+ wasmtime_wasi_io::bindings::wasi::io::streams::add_to_linker_get_host(l, io_closure)?;
+
let closure = type_annotate::(|t| WasiImpl(IoImpl(t)));
crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
crate::bindings::filesystem::types::add_to_linker_get_host(l, closure)?;
crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
- crate::bindings::io::error::add_to_linker_get_host(l, io_closure)?;
- crate::bindings::io::poll::add_to_linker_get_host(l, io_closure)?;
- crate::bindings::io::streams::add_to_linker_get_host(l, io_closure)?;
crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
@@ -405,15 +406,17 @@ pub fn add_to_linker_with_options_sync(
) -> anyhow::Result<()> {
let l = linker;
let io_closure = io_type_annotate::(|t| IoImpl(t));
+ wasmtime_wasi_io::bindings::wasi::io::error::add_to_linker_get_host(l, io_closure)?;
+
+ crate::bindings::sync::io::poll::add_to_linker_get_host(l, io_closure)?;
+ crate::bindings::sync::io::streams::add_to_linker_get_host(l, io_closure)?;
+
let closure = type_annotate::(|t| WasiImpl(IoImpl(t)));
crate::bindings::clocks::wall_clock::add_to_linker_get_host(l, closure)?;
crate::bindings::clocks::monotonic_clock::add_to_linker_get_host(l, closure)?;
crate::bindings::sync::filesystem::types::add_to_linker_get_host(l, closure)?;
crate::bindings::filesystem::preopens::add_to_linker_get_host(l, closure)?;
- crate::bindings::io::error::add_to_linker_get_host(l, io_closure)?;
- crate::bindings::sync::io::poll::add_to_linker_get_host(l, io_closure)?;
- crate::bindings::sync::io::streams::add_to_linker_get_host(l, io_closure)?;
crate::bindings::random::random::add_to_linker_get_host(l, closure)?;
crate::bindings::random::insecure::add_to_linker_get_host(l, closure)?;
crate::bindings::random::insecure_seed::add_to_linker_get_host(l, closure)?;
diff --git a/crates/wasi/src/pipe.rs b/crates/wasi/src/pipe.rs
index c09fc1c33b94..7b0abb1a4e8d 100644
--- a/crates/wasi/src/pipe.rs
+++ b/crates/wasi/src/pipe.rs
@@ -7,12 +7,14 @@
//! Some convenience constructors are included for common backing types like `Vec` and `String`,
//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
//!
-use crate::poll::Subscribe;
-use crate::{HostInputStream, HostOutputStream, StreamError};
use anyhow::anyhow;
use bytes::Bytes;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
+use wasmtime_wasi_io::{
+ poll::Subscribe,
+ stream::{HostInputStream, HostOutputStream, StreamError},
+};
pub use crate::write_stream::AsyncWriteStream;
diff --git a/crates/wasi/src/poll.rs b/crates/wasi/src/poll.rs
index de80269434f0..41804e289681 100644
--- a/crates/wasi/src/poll.rs
+++ b/crates/wasi/src/poll.rs
@@ -1,29 +1,29 @@
-pub mod sync {
- use crate::{bindings::io::poll as async_poll, runtime::in_tokio, IoImpl, IoView};
- use anyhow::Result;
- use wasmtime::component::Resource;
+use crate::runtime::in_tokio;
+use wasmtime_wasi_io::{bindings::wasi::io::poll as async_poll, poll::Pollable, IoImpl, IoView};
- impl poll::Host for IoImpl
- where
- T: IoView,
- {
- fn poll(&mut self, pollables: Vec>) -> Result> {
- in_tokio(async { async_poll::Host::poll(self, pollables).await })
- }
+use anyhow::Result;
+use wasmtime::component::Resource;
+
+impl crate::bindings::sync::io::poll::Host for IoImpl
+where
+ T: IoView,
+{
+ fn poll(&mut self, pollables: Vec>) -> Result> {
+ in_tokio(async { async_poll::Host::poll(self, pollables).await })
}
+}
- impl crate::bindings::sync::io::poll::HostPollable for IoImpl
- where
- T: IoView,
- {
- fn ready(&mut self, pollable: Resource) -> Result {
- in_tokio(async { async_poll::HostPollable::ready(self, pollable).await })
- }
- fn block(&mut self, pollable: Resource) -> Result<()> {
- in_tokio(async { async_poll::HostPollable::block(self, pollable).await })
- }
- fn drop(&mut self, pollable: Resource) -> Result<()> {
- async_poll::HostPollable::drop(self, pollable)
- }
+impl crate::bindings::sync::io::poll::HostPollable for IoImpl
+where
+ T: IoView,
+{
+ fn ready(&mut self, pollable: Resource) -> Result {
+ in_tokio(async { async_poll::HostPollable::ready(self, pollable).await })
+ }
+ fn block(&mut self, pollable: Resource) -> Result<()> {
+ in_tokio(async { async_poll::HostPollable::block(self, pollable).await })
+ }
+ fn drop(&mut self, pollable: Resource) -> Result<()> {
+ async_poll::HostPollable::drop(self, pollable)
}
}
diff --git a/crates/wasi/src/preview1.rs b/crates/wasi/src/preview1.rs
index 838875d11342..7ae6ed96f4e0 100644
--- a/crates/wasi/src/preview1.rs
+++ b/crates/wasi/src/preview1.rs
@@ -70,12 +70,8 @@ use crate::bindings::{
},
clocks::{monotonic_clock, wall_clock},
filesystem::{preopens::Host as _, types as filesystem},
- io::streams,
-};
-use crate::{
- FsError, IoImpl, IoView, IsATTY, ResourceTable, StreamError, StreamResult, WasiCtx, WasiImpl,
- WasiView,
};
+use crate::{FsError, IsATTY, ResourceTable, WasiCtx, WasiImpl, WasiView};
use anyhow::{bail, Context};
use std::collections::{BTreeMap, HashSet};
use std::mem::{self, size_of, size_of_val};
@@ -85,14 +81,19 @@ use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use system_interface::fs::FileIoExt;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::{
+ bindings::wasi::io::streams,
+ stream::{StreamError, StreamResult},
+ IoImpl, IoView,
+};
use wiggle::tracing::instrument;
use wiggle::{GuestError, GuestMemory, GuestPtr, GuestType};
// Bring all WASI traits in scope that this implementation builds on.
use crate::bindings::cli::environment::Host as _;
use crate::bindings::filesystem::types::HostDescriptor as _;
-use crate::bindings::io::poll::Host as _;
use crate::bindings::random::random::Host as _;
+use wasmtime_wasi_io::bindings::wasi::io::poll::Host as _;
/// Structure containing state for WASIp1.
///
diff --git a/crates/wasi/src/stdio/worker_thread_stdin.rs b/crates/wasi/src/stdio/worker_thread_stdin.rs
index 894bdd61affd..63179365e093 100644
--- a/crates/wasi/src/stdio/worker_thread_stdin.rs
+++ b/crates/wasi/src/stdio/worker_thread_stdin.rs
@@ -23,14 +23,16 @@
//! This module is one that's likely to change over time though as new systems
//! are encountered along with preexisting bugs.
-use crate::poll::Subscribe;
use crate::stdio::StdinStream;
-use crate::{HostInputStream, StreamError};
use bytes::{Bytes, BytesMut};
use std::io::{IsTerminal, Read};
use std::mem;
use std::sync::{Condvar, Mutex, OnceLock};
use tokio::sync::Notify;
+use wasmtime_wasi_io::{
+ poll::Subscribe,
+ stream::{HostInputStream, StreamError},
+};
#[derive(Default)]
struct GlobalStdin {
diff --git a/crates/wasi/src/udp.rs b/crates/wasi/src/udp.rs
index 1e0b1a199cf4..a53b33585741 100644
--- a/crates/wasi/src/udp.rs
+++ b/crates/wasi/src/udp.rs
@@ -1,5 +1,4 @@
use crate::host::network::util;
-use crate::poll::Subscribe;
use crate::runtime::with_ambient_tokio_runtime;
use async_trait::async_trait;
use cap_net_ext::{AddressFamily, Blocking};
@@ -7,6 +6,7 @@ use io_lifetimes::raw::{FromRawSocketlike, IntoRawSocketlike};
use std::io;
use std::net::SocketAddr;
use std::sync::Arc;
+use wasmtime_wasi_io::poll::Subscribe;
use super::network::{SocketAddrCheck, SocketAddressFamily};
diff --git a/crates/wasi/src/view.rs b/crates/wasi/src/view.rs
index 3a39c73dc360..d66aff246d7b 100644
--- a/crates/wasi/src/view.rs
+++ b/crates/wasi/src/view.rs
@@ -1,4 +1,5 @@
use crate::ctx::WasiCtx;
+use wasmtime::component::ResourceTable;
pub use wasmtime_wasi_io::{IoImpl, IoView};
pub trait WasiView: IoView {
From 793b0290dfda6eeed2f57d773a57f23721986fdd Mon Sep 17 00:00:00 2001
From: Pat Hickey
Date: Wed, 15 Jan 2025 17:56:32 -0800
Subject: [PATCH 06/27] redirect wasmtime_wasi's bindgen properly over to
wasmtime_wasi_io
---
crates/wasi/src/bindings.rs | 33 ++++++++++++++++++------------
crates/wasi/src/host/filesystem.rs | 2 +-
crates/wasi/src/host/tcp.rs | 7 +++++--
crates/wasi/src/stdio.rs | 8 ++++----
4 files changed, 30 insertions(+), 20 deletions(-)
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
index 72d7941c151f..6519f5b60458 100644
--- a/crates/wasi/src/bindings.rs
+++ b/crates/wasi/src/bindings.rs
@@ -160,12 +160,11 @@ pub mod sync {
},
trappable_imports: true,
with: {
- // These interfaces come from the outer module, as it's
- // sync/async agnostic.
+ // These interfaces contain only synchronous methods, so they
+ // can be aliased directly
"wasi:clocks": crate::bindings::clocks,
"wasi:random": crate::bindings::random,
"wasi:cli": crate::bindings::cli,
- "wasi:io/error": ::wasmtime_wasi_io::bindings::wasi::io::error,
"wasi:filesystem/preopens": crate::bindings::filesystem::preopens,
"wasi:sockets/network": crate::bindings::sockets::network,
@@ -174,13 +173,19 @@ pub mod sync {
// way everything has the same type.
"wasi:filesystem/types/descriptor": super::super::filesystem::types::Descriptor,
"wasi:filesystem/types/directory-entry-stream": super::super::filesystem::types::DirectoryEntryStream,
- "wasi:io/poll/pollable": wasmtime_wasi_io::poll::Pollable,
- "wasi:io/streams/input-stream": wasmtime_wasi_io::stream::InputStream,
- "wasi:io/streams/output-stream": wasmtime_wasi_io::stream::OutputStream,
"wasi:sockets/tcp/tcp-socket": super::super::sockets::tcp::TcpSocket,
"wasi:sockets/udp/incoming-datagram-stream": super::super::sockets::udp::IncomingDatagramStream,
"wasi:sockets/udp/outgoing-datagram-stream": super::super::sockets::udp::OutgoingDatagramStream,
"wasi:sockets/udp/udp-socket": super::super::sockets::udp::UdpSocket,
+
+ // Error host trait from wasmtime-wasi-io is synchronous, so we can alias it
+ "wasi:io/error": wasmtime_wasi_io::bindings::wasi::io::error,
+ // Configure the resource types from wasmtime-wasi-io, though
+ // this bindgen will make a new synchronous Host traits
+ "wasi:io/poll/pollable": wasmtime_wasi_io::poll::Pollable,
+ "wasi:io/streams/input-stream": wasmtime_wasi_io::stream::InputStream,
+ "wasi:io/streams/output-stream": wasmtime_wasi_io::stream::OutputStream,
+
},
require_store_data_send: true,
});
@@ -405,9 +410,15 @@ mod async_io {
"wasi:sockets/network/error-code" => crate::SocketError,
},
with: {
- // Configure all resources to be concrete types defined in this crate,
- // so that way we get to use nice typed helper methods with
- // `ResourceTable`.
+ // All interfaces in the wasi:io package should be aliased to
+ // the wasmtime-wasi-io generated code. Note that this will also
+ // map the resource types to those defined in that crate as well.
+ "wasi:io/poll": wasmtime_wasi_io::bindings::wasi::io::poll,
+ "wasi:io/streams": wasmtime_wasi_io::bindings::wasi::io::streams,
+ "wasi:io/error": wasmtime_wasi_io::bindings::wasi::io::error,
+
+ // Configure all other resources to be concrete types defined in
+ // this crate
"wasi:sockets/network/network": crate::network::Network,
"wasi:sockets/tcp/tcp-socket": crate::tcp::TcpSocket,
"wasi:sockets/udp/udp-socket": crate::udp::UdpSocket,
@@ -416,10 +427,6 @@ mod async_io {
"wasi:sockets/ip-name-lookup/resolve-address-stream": crate::ip_name_lookup::ResolveAddressStream,
"wasi:filesystem/types/directory-entry-stream": crate::filesystem::ReaddirIterator,
"wasi:filesystem/types/descriptor": crate::filesystem::Descriptor,
- "wasi:io/streams/input-stream": wasmtime_wasi_io::stream::InputStream,
- "wasi:io/streams/output-stream": wasmtime_wasi_io::stream::OutputStream,
- "wasi:io/error/error": wasmtime_wasi_io::stream::Error,
- "wasi:io/poll/pollable": wasmtime_wasi_io::poll::Pollable,
"wasi:cli/terminal-input/terminal-input": crate::stdio::TerminalInput,
"wasi:cli/terminal-output/terminal-output": crate::stdio::TerminalOutput,
},
diff --git a/crates/wasi/src/host/filesystem.rs b/crates/wasi/src/host/filesystem.rs
index a8cfb91754f3..add96be1eff4 100644
--- a/crates/wasi/src/host/filesystem.rs
+++ b/crates/wasi/src/host/filesystem.rs
@@ -3,13 +3,13 @@ use crate::bindings::filesystem::preopens;
use crate::bindings::filesystem::types::{
self, ErrorCode, HostDescriptor, HostDirectoryEntryStream,
};
-use crate::bindings::io::streams::{InputStream, OutputStream};
use crate::filesystem::{
Descriptor, Dir, File, FileInputStream, FileOutputStream, OpenMode, ReaddirIterator,
};
use crate::{DirPerms, FilePerms, FsError, FsResult, IoView, WasiImpl, WasiView};
use anyhow::Context;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::stream::{InputStream, OutputStream};
mod sync;
diff --git a/crates/wasi/src/host/tcp.rs b/crates/wasi/src/host/tcp.rs
index 123c71ce1eb1..0263ba24817e 100644
--- a/crates/wasi/src/host/tcp.rs
+++ b/crates/wasi/src/host/tcp.rs
@@ -1,7 +1,6 @@
use crate::network::SocketAddrUse;
use crate::{
bindings::{
- io::streams::{InputStream, OutputStream},
sockets::network::{IpAddressFamily, IpSocketAddress, Network},
sockets::tcp::{self, ShutdownType},
},
@@ -11,7 +10,11 @@ use crate::{SocketResult, WasiImpl, WasiView};
use std::net::SocketAddr;
use std::time::Duration;
use wasmtime::component::Resource;
-use wasmtime_wasi_io::{poll::Pollable, IoView};
+use wasmtime_wasi_io::{
+ poll::Pollable,
+ stream::{InputStream, OutputStream},
+ IoView,
+};
impl tcp::Host for WasiImpl where T: WasiView {}
diff --git a/crates/wasi/src/stdio.rs b/crates/wasi/src/stdio.rs
index 17ab5820db73..7fed39adc084 100644
--- a/crates/wasi/src/stdio.rs
+++ b/crates/wasi/src/stdio.rs
@@ -2,7 +2,6 @@ use crate::bindings::cli::{
stderr, stdin, stdout, terminal_input, terminal_output, terminal_stderr, terminal_stdin,
terminal_stdout,
};
-use crate::bindings::io::streams;
use crate::pipe;
use crate::{
HostInputStream, HostOutputStream, IoView, StreamError, StreamResult, Subscribe, WasiImpl,
@@ -13,6 +12,7 @@ use std::io::IsTerminal;
use std::sync::Arc;
use tokio::sync::Mutex;
use wasmtime::component::Resource;
+use wasmtime_wasi_io::stream;
/// A trait used to represent the standard input to a guest program.
///
@@ -414,7 +414,7 @@ impl stdin::Host for WasiImpl
where
T: WasiView,
{
- fn get_stdin(&mut self) -> Result, anyhow::Error> {
+ fn get_stdin(&mut self) -> Result, anyhow::Error> {
let stream = self.ctx().stdin.stream();
Ok(self.table().push(stream)?)
}
@@ -424,7 +424,7 @@ impl stdout::Host for WasiImpl
where
T: WasiView,
{
- fn get_stdout(&mut self) -> Result, anyhow::Error> {
+ fn get_stdout(&mut self) -> Result