Skip to content

Implement io module for WASIp3 - #162

Merged
adamrk merged 6 commits into
mainfrom
abk/io-p3
Oct 1, 2026
Merged

adamrk merged 6 commits into
mainfrom
abk/io-p3

Conversation

@adamrk

@adamrk adamrk commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Resolves #155

Here the p2 and p3 backends have completely different implementations because the underlying streams are quite different. Notably, flush becomes a no-op on p3 streams because they have no notion of flushing. However, we are able to implement flush for Stdout and Stderr.

Comment thread test-programs/build.rs
Comment on lines 6 to +20
@@ -16,13 +17,13 @@ fn main() {
meta.workspace_root.as_os_str().to_str().unwrap()
);

fn build_targets(pkg: &str, manifest: &str, kind: &str, out_dir: &PathBuf) {
fn build_target(pkg: &str, manifest: &str, kind: &str, target: &str, out_dir: &Path) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to make some changes here to get the test programs running for both targets. We also need to detect the nightly toolchain to only run the p3 tests when using nightly.

@pchickey
pchickey added this pull request to stack #165 September 25, 2026 18:20

@pchickey pchickey left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some suggestions but they're all pretty debatable, let me know what you think

Comment thread src/io/mod.rs Outdated
mod seek;
mod stdio;
mod streams;
#[cfg(target_env = "p2")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its a matter of taste, but I think the rough pattern @yoshuawuyts used, borrowed from Rust std, would be to have (paraphrasing) mod streams { mod sys { #[cfg(p2)] mod p2 { ... }, #[cfg(p3)] mod p3 { ... } } #[cfg(p2)] use sys::p2::*; #[cfg(p3)] use sys::p3::*; } is the idiom we should adopt when there are distinct implementations that get cfg-dispatched at the unit of modules.

Comment thread src/io/stdio.rs Outdated
#[test]
// No internal predicate. Run test with --nocapture and inspect output manually.
fn stderr_println_hello_world() {
block_on(async {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for spotting this

Comment thread src/io/stdio.rs Outdated
@@ -1,16 +1,40 @@
use super::{AsyncInputStream, AsyncOutputStream, AsyncRead, AsyncWrite, Result};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It comes down to taste and I'm not sure how much of a nag this is, but my gut says the small amount of code reuse in this file between p2 and p3 doesn't overcome the considerable complexity of all the cfgs throughout. I feel like this would be better in the long term as two distinct mods (with the sys dispatch pattern) as well? Open to pushback

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this one ended up messier than I initially thought. Also changed it to use the sys pattern.

Comment thread src/io/streams/sys/p3.rs

#[test]
fn chunk_stream_partial_read_works() {
crate::runtime::block_on(async {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love how we can do this from inside the guest now!!

Comment thread test-programs/tests/stdio.rs Outdated

#[test_log::test]
fn stdio_p3() -> Result<()> {
// TODO: Remove this nightly check once wasm32-wasip3 is available on stable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using cfg means we could make this #[cfg_attr(not(wstd_nightly), ignore] which feels better than a const to me

Comment thread test-programs/build.rs Outdated
);

let mut generated_code = "// THIS FILE IS GENERATED CODE\n".to_string();
generated_code += &format!("pub const NIGHTLY_TOOLCHAIN: bool = {nightly_toolchain};\n\n");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this dispatch with a const, could we instead be invoking cargo build with https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg? And maybe have a cfg flag wstd_nightly to gate this stuff instead of a const

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess in CI we could always run cargo with --cfg wstd_nightly when compiling with nightly, but then when testing locally it'll be a bit annoying to remember it probably? Adding it to the build command we run in build.rs would only affect the examples so we still couldn't use it in the tests. Or is there another way I'm missing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also have test-programs/build.rs apply it to all of the test-programs crate with https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got that working now.

@adamrk
adamrk requested a review from pchickey September 30, 2026 14:39
@adamrk
adamrk merged commit deb673f into main Oct 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement io on WASIp3

2 participants