Stream: wasmtime

Topic: ✔ async API revamp implications


view this post on Zulip Joel Dice (Aug 27 2025 at 14:14):

Yeah, that's an interesting observation. I think it aligns pretty well with the vision for stream<t, u, v> where you're expected to either read or skip to the end of the stream before you can get the final u value, so I wouldn't consider it a problem.

Regarding GC languages: the ones I'm familiar with usually do have some sort of close function for input streams, and that generally integrates with the language's scoped resource handling, if available.

view this post on Zulip Joel Dice (Aug 27 2025 at 14:16):

But per your latest comment on the issue: yeah, you shouldn't have to drop the stream necessarily -- just read or skip to the end.

view this post on Zulip Joel Dice (Aug 27 2025 at 14:16):

I might need to tweak the implementation if that's not working.

view this post on Zulip Roman Volosatovs (Aug 27 2025 at 14:19):

If you want, we could jump on a quick call and I can show you the behavior I'm seeing?
Otherwise, you can reproduce this by removing any of the drops I've added in the commit. all p3_sockets tests currently pass and without the drop tcp_sample_application deadlocks

you'd likely also want to do something like:

diff --git a/crates/wasi/tests/all/p3/mod.rs b/crates/wasi/tests/all/p3/mod.rs
index 4efc033968..87e56b3d43 100644
--- a/crates/wasi/tests/all/p3/mod.rs
+++ b/crates/wasi/tests/all/p3/mod.rs
@@ -20,7 +20,7 @@ async fn run(path: &str) -> Result<()> {
     wasmtime_wasi::p3::add_to_linker(&mut linker).context("failed to link `wasi:cli@0.3.x`")?;

     let (mut store, _td) = Ctx::new(&engine, name, |builder| MyWasiCtx {
-        wasi: builder.build(),
+        wasi: builder.inherit_stdout().inherit_stderr().build(),
         table: Default::default(),
     })?;
     let component = Component::from_file(&engine, path)?;

to actually see the output from the component if you were to resort to print-debugging

view this post on Zulip Joel Dice (Aug 27 2025 at 14:20):

Yeah, I'll repro and debug and then let you know what I find.

view this post on Zulip Roman Volosatovs (Aug 27 2025 at 14:22):

But generally, the behavior right now makes sense to me - dropping the stream handle causes the socket shutdown, as specified in wasi:sockets. Since we have not encountered any errors, there's not really a reason for the future to return anything yet. Basically, the only way I see this not requiring the drop of the stream handle is host buffering data from the OS

view this post on Zulip Joel Dice (Aug 27 2025 at 14:24):

ah, ok, so the test is not necessarily reading to the end (i.e. not reading until it gets a StreamResult::Dropped), correct? In that case, yeah, it makes sense that you'd need to drop the stream explicitly before awaiting the future.

view this post on Zulip Roman Volosatovs (Aug 27 2025 at 14:24):

yes, exactly, I'm just adding that to the test as we speak

view this post on Zulip Roman Volosatovs (Aug 27 2025 at 14:26):

diff --git a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs
index 83d51fbcf8..2fe38cb855 100644
--- a/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs
+++ b/crates/test-programs/src/bin/p3_sockets_tcp_sample_application.rs
@@ -44,11 +44,14 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo
             let (mut data_rx, fut) = sock.receive();
             let (result, data) = data_rx.read(Vec::with_capacity(100)).await;
             assert_eq!(result, StreamResult::Complete(first_message.len()));
-
             // Check that we sent and received our message!
             assert_eq!(data, first_message); // Not guaranteed to work but should work in practice.
-            drop(data_rx);
-            fut.await.unwrap()
+
+            let (result, data) = data_rx.read(Vec::with_capacity(1)).await;
+            assert_eq!(result, StreamResult::Dropped);
+            assert_eq!(data, []);
+
+            fut.await.unwrap();
         },
     );

@@ -74,10 +77,13 @@ async fn test_tcp_sample_application(family: IpAddressFamily, bind_address: IpSo
             let (mut data_rx, fut) = sock.receive();
             let (result, data) = data_rx.read(Vec::with_capacity(100)).await;
             assert_eq!(result, StreamResult::Complete(second_message.len()));
-
             // Check that we sent and received our message!
             assert_eq!(data, second_message); // Not guaranteed to work but should work in practice.
-            drop(data_rx);
+
+            let (result, data) = data_rx.read(Vec::with_capacity(1)).await;
+            assert_eq!(result, StreamResult::Dropped);
+            assert_eq!(data, []);
+
             fut.await.unwrap()
         }
     );

this works, and IMO that's actually the correct behavior - previous implementation was working "by accident"

view this post on Zulip Joel Dice (Aug 27 2025 at 14:27):

yeah, looking at the code I would expect that to work, so glad to know it does

view this post on Zulip Joel Dice (Aug 27 2025 at 14:29):

also good to know that drop(data_rx) is effectively equivalent to "skip to the end; I don't care about the rest", since that's more efficient than reading and ignoring the items.

view this post on Zulip Alex Crichton (Aug 27 2025 at 14:29):

this all makes sense to me so far, but what's up with the last diff you pasted roman?

view this post on Zulip Alex Crichton (Aug 27 2025 at 14:29):

is that required to make the test pass?

view this post on Zulip Joel Dice (Aug 27 2025 at 14:30):

I think the upshot is that you either need to drop the stream or read to the end before you await the future

view this post on Zulip Alex Crichton (Aug 27 2025 at 14:30):

so either drop now or get StreamResult::Dropped before the future resolves?

view this post on Zulip Joel Dice (Aug 27 2025 at 14:30):

right

view this post on Zulip Alex Crichton (Aug 27 2025 at 14:30):

yeah that makes sense to me

view this post on Zulip Joel Dice (Aug 27 2025 at 14:30):

i.e. both the before and after for that diff should work

view this post on Zulip Roman Volosatovs (Aug 27 2025 at 14:32):

previously the test was not doing either and it just happened to work, because the host task was buffering data from the host and therefore it was notified about the shutdown earlier

view this post on Zulip Notification Bot (Aug 27 2025 at 14:32):

Roman Volosatovs has marked this topic as resolved.

view this post on Zulip Alex Crichton (Aug 27 2025 at 14:33):

I suspect that these sorts of minor tweaks in behavior are going to crop up over time, even after we ship WASIp3


Last updated: Dec 06 2025 at 06:05 UTC