Async write prevents Rust bindings and component composition #870
+6
−6
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Several WASI interfaces (stdio, filesystem, sockets) use the following pattern:
readis synchronous and returns astreamandfuturethat are independent of the implicitthishandle.writeis asynchronous and implicitly borrowsthisfor the entire duration of consuming the input stream.Problem: Component composition
Because
writekeepsthisborrowed until the input stream is fully consumed, the resource and its owning component must stay alive for the entire stream operation. This prevents a component from performing a one-time setup, forwarding streams, and then returning immediately. In middleware-style patterns, the component cannot "step out of the way" once the streams are connected up, since the asyncwriteties the stream's lifetime to the component's presence.Problem: Rust lifetimes
On the Rust side, this pattern makes it impossible to wrap such a resource into a single struct without resorting to
unsafecode. Thewritecall produces a future that captures a borrow ofthis, which makes theFutureself-referential with respect to the resource being stored. For example:Conceptually, the
send_resultfuture is a self-borrow ofsocket. This pattern cannot be expressed safely in Rust today and prevents ergonomic bindings for common I/O abstractions likeAsyncReadandAsyncWrite.Solution
The most straight-forward thing I could come up with is to turn the
writemethods synchronous and return afutureinstead, mirroring thereadmethods. Other thoughts are welcome too of course.From e.g.:
write: async func(data: stream<u8>) -> result<_, error-code>;To:
write: func(data: stream<u8>) -> future<result<_, error-code>>;WDTY?