diff --git a/crates/test-programs/src/bin/p1_cli_hostcall_fuel.rs b/crates/test-programs/src/bin/p1_cli_hostcall_fuel.rs index 9fb93efe897d..ef7e36002533 100644 --- a/crates/test-programs/src/bin/p1_cli_hostcall_fuel.rs +++ b/crates/test-programs/src/bin/p1_cli_hostcall_fuel.rs @@ -1,9 +1,11 @@ use std::ptr; +use test_programs::preview1::BlockingMode; fn main() { big_poll(); big_string(); - big_iovecs(); + big_iovecs(BlockingMode::Blocking); + big_iovecs(BlockingMode::NonBlocking); } fn big_string() { @@ -18,7 +20,7 @@ fn big_string() { ); } -fn big_iovecs() { +fn big_iovecs(blocking_mode: BlockingMode) { let mut iovs = Vec::new(); let mut ciovs = Vec::new(); for _ in 0..10_000 { @@ -40,14 +42,14 @@ fn big_iovecs() { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_WRITE | wasip1::RIGHTS_FD_READ, 0, - 0, + blocking_mode.fd_flags(), ) .unwrap() }; unsafe { - assert_eq!(wasip1::fd_write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM)); - assert_eq!(wasip1::fd_read(fd, &iovs), Err(wasip1::ERRNO_NOMEM)); + assert_eq!(blocking_mode.write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM)); + assert_eq!(blocking_mode.read(fd, &iovs), Err(wasip1::ERRNO_NOMEM)); assert_eq!(wasip1::fd_pwrite(fd, &ciovs, 0), Err(wasip1::ERRNO_NOMEM)); assert_eq!(wasip1::fd_pread(fd, &iovs, 0), Err(wasip1::ERRNO_NOMEM)); } @@ -63,8 +65,8 @@ fn big_iovecs() { buf_len: 10_000, }); unsafe { - assert_eq!(wasip1::fd_write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM)); - assert_eq!(wasip1::fd_read(fd, &iovs), Err(wasip1::ERRNO_NOMEM)); + assert_eq!(blocking_mode.write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM)); + assert_eq!(blocking_mode.read(fd, &iovs), Err(wasip1::ERRNO_NOMEM)); assert_eq!(wasip1::fd_pwrite(fd, &ciovs, 0), Err(wasip1::ERRNO_NOMEM)); assert_eq!(wasip1::fd_pread(fd, &iovs, 0), Err(wasip1::ERRNO_NOMEM)); } diff --git a/crates/test-programs/src/bin/p1_fd_flags_set.rs b/crates/test-programs/src/bin/p1_fd_flags_set.rs index 8770f2450c27..ae0f13b5fb3f 100644 --- a/crates/test-programs/src/bin/p1_fd_flags_set.rs +++ b/crates/test-programs/src/bin/p1_fd_flags_set.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::open_scratch_directory; +use test_programs::preview1::{BlockingMode, open_scratch_directory}; -unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { +unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { const FILE_NAME: &str = "file"; let data = &[0u8; 100]; @@ -14,20 +14,21 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - wasip1::FDFLAGS_APPEND, + blocking_mode.fd_flags() | wasip1::FDFLAGS_APPEND, ) .expect("opening a file"); // Write some data and then verify the written data assert_eq!( - wasip1::fd_write( - file_fd, - &[wasip1::Ciovec { - buf: data.as_ptr(), - buf_len: data.len(), - }], - ) - .expect("writing to a file"), + blocking_mode + .write( + file_fd, + &[wasip1::Ciovec { + buf: data.as_ptr(), + buf_len: data.len(), + }], + ) + .expect("writing to a file"), data.len(), "should write {} bytes", data.len(), @@ -38,14 +39,15 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { let buffer = &mut [0u8; 100]; assert_eq!( - wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }] - ) - .expect("reading file"), + blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }] + ) + .expect("reading file"), buffer.len(), "should read {} bytes", buffer.len() @@ -59,14 +61,15 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_SET).expect("seeking file"); assert_eq!( - wasip1::fd_write( - file_fd, - &[wasip1::Ciovec { - buf: data.as_ptr(), - buf_len: data.len(), - }], - ) - .expect("writing to a file"), + blocking_mode + .write( + file_fd, + &[wasip1::Ciovec { + buf: data.as_ptr(), + buf_len: data.len(), + }], + ) + .expect("writing to a file"), data.len(), "should write {} bytes", data.len(), @@ -75,14 +78,15 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { wasip1::fd_seek(file_fd, 100, wasip1::WHENCE_SET).expect("seeking file"); assert_eq!( - wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }] - ) - .expect("reading file"), + blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }] + ) + .expect("reading file"), buffer.len(), "should read {} bytes", buffer.len() @@ -90,7 +94,7 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { assert_eq!(&data[..], &buffer[..]); - wasip1::fd_fdstat_set_flags(file_fd, 0).expect("disabling flags"); + wasip1::fd_fdstat_set_flags(file_fd, blocking_mode.fd_flags()).expect("disabling flags"); // Overwrite some existing data to ensure the append mode is now off wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_SET).expect("seeking file"); @@ -98,14 +102,15 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { let data = &[2u8; 100]; assert_eq!( - wasip1::fd_write( - file_fd, - &[wasip1::Ciovec { - buf: data.as_ptr(), - buf_len: data.len(), - }], - ) - .expect("writing to a file"), + blocking_mode + .write( + file_fd, + &[wasip1::Ciovec { + buf: data.as_ptr(), + buf_len: data.len(), + }], + ) + .expect("writing to a file"), data.len(), "should write {} bytes", data.len(), @@ -114,14 +119,15 @@ unsafe fn test_fd_fdstat_set_flags(dir_fd: wasip1::Fd) { wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_SET).expect("seeking file"); assert_eq!( - wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }] - ) - .expect("reading file"), + blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }] + ) + .expect("reading file"), buffer.len(), "should read {} bytes", buffer.len() @@ -157,6 +163,7 @@ fn main() { }; unsafe { - test_fd_fdstat_set_flags(dir_fd); + test_fd_fdstat_set_flags(dir_fd, BlockingMode::Blocking); + test_fd_fdstat_set_flags(dir_fd, BlockingMode::NonBlocking); } } diff --git a/crates/test-programs/src/bin/p1_file_read_write.rs b/crates/test-programs/src/bin/p1_file_read_write.rs index 0ca2f1eefa39..abb008e311bd 100644 --- a/crates/test-programs/src/bin/p1_file_read_write.rs +++ b/crates/test-programs/src/bin/p1_file_read_write.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::open_scratch_directory; +use test_programs::preview1::{BlockingMode, open_scratch_directory}; -unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { +unsafe fn test_file_read_write(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { // Create a file in the scratch directory. let file_fd = wasip1::path_open( dir_fd, @@ -12,7 +12,7 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("opening a file"); assert!( @@ -25,7 +25,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf: contents.as_ptr() as *const _, buf_len: contents.len(), }; - let mut nwritten = wasip1::fd_write(file_fd, &[ciovec]).expect("writing bytes at offset 0"); + let mut nwritten = blocking_mode + .write(file_fd, &[ciovec]) + .expect("writing bytes at offset 0"); assert_eq!(nwritten, 4, "nwritten bytes check"); let contents = &mut [0u8; 4]; @@ -34,7 +36,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: contents.len(), }; wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_SET).expect("seeking to offset 0"); - let mut nread = wasip1::fd_read(file_fd, &[iovec]).expect("reading bytes at offset 0"); + let mut nread = blocking_mode + .read(file_fd, &[iovec]) + .expect("reading bytes at offset 0"); assert_eq!(nread, 4, "nread bytes check"); assert_eq!(contents, &[0u8, 1, 2, 3], "written bytes equal read bytes"); @@ -61,8 +65,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: remaining, }); - nwritten = - wasip1::fd_write(file_fd, ciovecs.as_slice()).expect("writing bytes at offset 0"); + nwritten = blocking_mode + .write(file_fd, ciovecs.as_slice()) + .expect("writing bytes at offset 0"); offset += nwritten; if offset == contents.len() { @@ -91,7 +96,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: 2, }, ]; - nread = wasip1::fd_read(file_fd, iovecs).expect("reading bytes at offset 0"); + nread = blocking_mode + .read(file_fd, iovecs) + .expect("reading bytes at offset 0"); if nread == 0 { break; } @@ -107,7 +114,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: contents.len(), }; wasip1::fd_seek(file_fd, 2, wasip1::WHENCE_SET).expect("seeking to offset 2"); - nread = wasip1::fd_read(file_fd, &[iovec]).expect("reading bytes at offset 2"); + nread = blocking_mode + .read(file_fd, &[iovec]) + .expect("reading bytes at offset 2"); assert_eq!(nread, 2, "nread bytes check"); assert_eq!(contents, &[2u8, 3, 0, 0], "file cursor was overwritten"); @@ -117,7 +126,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: contents.len(), }; wasip1::fd_seek(file_fd, 2, wasip1::WHENCE_SET).expect("seeking to offset 2"); - nwritten = wasip1::fd_write(file_fd, &[ciovec]).expect("writing bytes at offset 2"); + nwritten = blocking_mode + .write(file_fd, &[ciovec]) + .expect("writing bytes at offset 2"); assert_eq!(nwritten, 2, "nwritten bytes check"); let contents = &mut [0u8; 4]; @@ -126,7 +137,9 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { buf_len: contents.len(), }; wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_SET).expect("seeking to offset 0"); - nread = wasip1::fd_read(file_fd, &[iovec]).expect("reading bytes at offset 0"); + nread = blocking_mode + .read(file_fd, &[iovec]) + .expect("reading bytes at offset 0"); assert_eq!(nread, 4, "nread bytes check"); assert_eq!(contents, &[0u8, 1, 1, 0], "file cursor was overwritten"); @@ -134,7 +147,7 @@ unsafe fn test_file_read_write(dir_fd: wasip1::Fd) { wasip1::path_unlink_file(dir_fd, "file").expect("removing a file"); } -unsafe fn test_file_write_and_file_pos(dir_fd: wasip1::Fd) { +unsafe fn test_file_write_and_file_pos(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { let path = "file2"; let file_fd = wasip1::path_open( dir_fd, @@ -160,7 +173,9 @@ unsafe fn test_file_write_and_file_pos(dir_fd: wasip1::Fd) { buf_len: 0, }; wasip1::fd_seek(file_fd, 2, wasip1::WHENCE_SET).expect("seeking to offset 2"); - let n = wasip1::fd_write(file_fd, &[ciovec]).expect("writing bytes at offset 2"); + let n = blocking_mode + .write(file_fd, &[ciovec]) + .expect("writing bytes at offset 2"); assert_eq!(n, 0); assert_eq!(wasip1::fd_tell(file_fd).unwrap(), 2); @@ -174,7 +189,9 @@ unsafe fn test_file_write_and_file_pos(dir_fd: wasip1::Fd) { buf_len: buf.len(), }; wasip1::fd_seek(file_fd, 50, wasip1::WHENCE_SET).expect("seeking to offset 50"); - let n = wasip1::fd_write(file_fd, &[ciovec]).expect("writing bytes at offset 50"); + let n = blocking_mode + .write(file_fd, &[ciovec]) + .expect("writing bytes at offset 50"); assert_eq!(n, 1); assert_eq!(wasip1::fd_tell(file_fd).unwrap(), 51); @@ -206,7 +223,9 @@ fn main() { // Run the tests. unsafe { - test_file_read_write(dir_fd); - test_file_write_and_file_pos(dir_fd); + test_file_read_write(dir_fd, BlockingMode::Blocking); + test_file_read_write(dir_fd, BlockingMode::NonBlocking); + test_file_write_and_file_pos(dir_fd, BlockingMode::Blocking); + test_file_write_and_file_pos(dir_fd, BlockingMode::NonBlocking); } } diff --git a/crates/test-programs/src/bin/p1_file_seek_tell.rs b/crates/test-programs/src/bin/p1_file_seek_tell.rs index 57630da029ad..b39b30827aa0 100644 --- a/crates/test-programs/src/bin/p1_file_seek_tell.rs +++ b/crates/test-programs/src/bin/p1_file_seek_tell.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{BlockingMode, assert_errno, open_scratch_directory}; -unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd) { +unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { // Create a file in the scratch directory. let file_fd = wasip1::path_open( dir_fd, @@ -12,7 +12,7 @@ unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("opening a file"); assert!( @@ -30,7 +30,9 @@ unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd) { buf: data.as_ptr() as *const _, buf_len: data.len(), }; - let nwritten = wasip1::fd_write(file_fd, &[iov]).expect("writing to a file"); + let nwritten = blocking_mode + .write(file_fd, &[iov]) + .expect("writing to a file"); assert_eq!(nwritten, 100, "should write 100 bytes to file"); // Check current offset @@ -72,7 +74,7 @@ unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd) { buf: buffer.as_mut_ptr(), buf_len: buffer.len(), }; - let nread = wasip1::fd_read(file_fd, &[iovec]).expect("reading file"); + let nread = blocking_mode.read(file_fd, &[iovec]).expect("reading file"); assert_eq!(nread, buffer.len(), "should read {} bytes", buffer.len()); offset = wasip1::fd_tell(file_fd).expect("getting file offset after reading"); @@ -84,7 +86,7 @@ unsafe fn test_file_seek_tell(dir_fd: wasip1::Fd) { // Test that when a file is opened with `O_APPEND` that acquiring the current // position indicates the end of the file. -unsafe fn seek_and_o_append(dir_fd: wasip1::Fd) { +unsafe fn seek_and_o_append(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { let path = "file2"; let file_fd = wasip1::path_open( dir_fd, @@ -93,7 +95,7 @@ unsafe fn seek_and_o_append(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - wasip1::FDFLAGS_APPEND, + blocking_mode.fd_flags() | wasip1::FDFLAGS_APPEND, ) .expect("opening a file"); assert!( @@ -111,7 +113,7 @@ unsafe fn seek_and_o_append(dir_fd: wasip1::Fd) { buf: data.as_ptr() as *const _, buf_len: data.len(), }; - let nwritten = wasip1::fd_write(file_fd, &[iov]).unwrap(); + let nwritten = blocking_mode.write(file_fd, &[iov]).unwrap(); assert_eq!(nwritten, 100); let mut offset = wasip1::fd_seek(file_fd, 0, wasip1::WHENCE_CUR).unwrap(); @@ -144,7 +146,9 @@ fn main() { // Run the tests. unsafe { - test_file_seek_tell(dir_fd); - seek_and_o_append(dir_fd); + test_file_seek_tell(dir_fd, BlockingMode::Blocking); + test_file_seek_tell(dir_fd, BlockingMode::NonBlocking); + seek_and_o_append(dir_fd, BlockingMode::Blocking); + seek_and_o_append(dir_fd, BlockingMode::NonBlocking); } } diff --git a/crates/test-programs/src/bin/p1_file_truncation.rs b/crates/test-programs/src/bin/p1_file_truncation.rs index bac026859963..6dbb85c7d342 100644 --- a/crates/test-programs/src/bin/p1_file_truncation.rs +++ b/crates/test-programs/src/bin/p1_file_truncation.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::open_scratch_directory; +use test_programs::preview1::{BlockingMode, open_scratch_directory}; -unsafe fn test_file_truncation(dir_fd: wasip1::Fd) { +unsafe fn test_file_truncation(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { const FILENAME: &str = "test.txt"; // Open a file for writing @@ -14,20 +14,21 @@ unsafe fn test_file_truncation(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("creating a file for writing"); // Write to the file let content = b"this content will be truncated!"; - let nwritten = wasip1::fd_write( - file_fd, - &[wasip1::Ciovec { - buf: content.as_ptr() as *const _, - buf_len: content.len(), - }], - ) - .expect("writing file content"); + let nwritten = blocking_mode + .write( + file_fd, + &[wasip1::Ciovec { + buf: content.as_ptr() as *const _, + buf_len: content.len(), + }], + ) + .expect("writing file content"); assert_eq!(nwritten, content.len(), "nwritten bytes check"); wasip1::fd_close(file_fd).expect("closing the file"); @@ -40,20 +41,21 @@ unsafe fn test_file_truncation(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT | wasip1::OFLAGS_TRUNC, wasip1::RIGHTS_FD_WRITE | wasip1::RIGHTS_FD_READ, 0, - 0, + blocking_mode.fd_flags(), ) .expect("creating a truncated file for reading"); // Read the file's contents let buffer = &mut [0u8; 100]; - let nread = wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }], - ) - .expect("reading file content"); + let nread = blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }], + ) + .expect("reading file content"); // The file should be empty due to truncation assert_eq!(nread, 0, "expected an empty file after truncation"); @@ -81,5 +83,8 @@ fn main() { }; // Run the tests. - unsafe { test_file_truncation(dir_fd) } + unsafe { + test_file_truncation(dir_fd, BlockingMode::Blocking); + test_file_truncation(dir_fd, BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/bin/p1_file_unbuffered_write.rs b/crates/test-programs/src/bin/p1_file_unbuffered_write.rs index d69c948e520a..713cffe4ab56 100644 --- a/crates/test-programs/src/bin/p1_file_unbuffered_write.rs +++ b/crates/test-programs/src/bin/p1_file_unbuffered_write.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::open_scratch_directory; +use test_programs::preview1::{BlockingMode, open_scratch_directory}; -unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd) { +unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { // Create and open file for reading let fd_read = wasip1::path_open( dir_fd, @@ -12,7 +12,7 @@ unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_READ, 0, - 0, + blocking_mode.fd_flags(), ) .expect("create and open file for reading"); assert!( @@ -21,8 +21,16 @@ unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd) { ); // Open the same file but for writing - let fd_write = wasip1::path_open(dir_fd, 0, "file", 0, wasip1::RIGHTS_FD_WRITE, 0, 0) - .expect("opening file for writing"); + let fd_write = wasip1::path_open( + dir_fd, + 0, + "file", + 0, + wasip1::RIGHTS_FD_WRITE, + 0, + blocking_mode.fd_flags(), + ) + .expect("opening file for writing"); assert!( fd_write > libc::STDERR_FILENO as wasip1::Fd, "file descriptor range check", @@ -34,7 +42,9 @@ unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd) { buf: contents.as_ptr() as *const _, buf_len: contents.len(), }; - let nwritten = wasip1::fd_write(fd_write, &[ciovec]).expect("writing byte to file"); + let nwritten = blocking_mode + .write(fd_write, &[ciovec]) + .expect("writing byte to file"); assert_eq!(nwritten, 1, "nwritten bytes check"); // Read from file @@ -43,7 +53,9 @@ unsafe fn test_file_unbuffered_write(dir_fd: wasip1::Fd) { buf: contents.as_mut_ptr() as *mut _, buf_len: contents.len(), }; - let nread = wasip1::fd_read(fd_read, &[iovec]).expect("reading bytes from file"); + let nread = blocking_mode + .read(fd_read, &[iovec]) + .expect("reading bytes from file"); assert_eq!(nread, 1, "nread bytes check"); assert_eq!(contents, &[1u8], "written bytes equal read bytes"); @@ -72,5 +84,8 @@ fn main() { }; // Run the tests. - unsafe { test_file_unbuffered_write(dir_fd) } + unsafe { + test_file_unbuffered_write(dir_fd, BlockingMode::Blocking); + test_file_unbuffered_write(dir_fd, BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/bin/p1_file_write.rs b/crates/test-programs/src/bin/p1_file_write.rs index ccc3629b8e1b..27a816385646 100644 --- a/crates/test-programs/src/bin/p1_file_write.rs +++ b/crates/test-programs/src/bin/p1_file_write.rs @@ -1,9 +1,9 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::open_scratch_directory; +use test_programs::preview1::{BlockingMode, open_scratch_directory}; -unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { +unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str, blocking_mode: BlockingMode) { // Open a file for writing let file_fd = wasip1::path_open( dir_fd, @@ -12,7 +12,7 @@ unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("creating a file for writing"); @@ -25,14 +25,15 @@ unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { } // Write to the file - let nwritten = wasip1::fd_write( - file_fd, - &[wasip1::Ciovec { - buf: content.as_slice().as_ptr() as *const _, - buf_len: content.len(), - }], - ) - .expect("writing file content"); + let nwritten = blocking_mode + .write( + file_fd, + &[wasip1::Ciovec { + buf: content.as_slice().as_ptr() as *const _, + buf_len: content.len(), + }], + ) + .expect("writing file content"); assert_eq!(nwritten, content.len(), "nwritten bytes check"); let stat = wasip1::fd_filestat_get(file_fd).expect("reading file stats"); @@ -44,19 +45,28 @@ unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { wasip1::fd_close(file_fd).expect("closing the file"); // Open the file for reading - let file_fd = wasip1::path_open(dir_fd, 0, filename, 0, wasip1::RIGHTS_FD_READ, 0, 0) - .expect("open the file for reading"); + let file_fd = wasip1::path_open( + dir_fd, + 0, + filename, + 0, + wasip1::RIGHTS_FD_READ, + 0, + blocking_mode.fd_flags(), + ) + .expect("open the file for reading"); // Read the file's contents let buffer = &mut [0u8; 100]; - let nread = wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }], - ) - .expect("reading first chunk file content"); + let nread = blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }], + ) + .expect("reading first chunk file content"); assert_eq!(nread, buffer.len(), "read first chunk"); assert_eq!( @@ -69,14 +79,15 @@ unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { wasip1::fd_seek(file_fd, end_cursor as i64, wasip1::WHENCE_SET) .expect("seeking to end of file minus buffer size"); - let nread = wasip1::fd_read( - file_fd, - &[wasip1::Iovec { - buf: buffer.as_mut_ptr(), - buf_len: buffer.len(), - }], - ) - .expect("reading end chunk of file content"); + let nread = blocking_mode + .read( + file_fd, + &[wasip1::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: buffer.len(), + }], + ) + .expect("reading end chunk of file content"); assert_eq!(nread, buffer.len(), "read end chunk len"); assert_eq!(buffer, &content[end_cursor..], "contents of end read chunk"); @@ -92,13 +103,21 @@ unsafe fn test_file_long_write(dir_fd: wasip1::Fd, filename: &str) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("creating a file for writing"); wasip1::fd_close(file_fd).expect("closing the file"); - let file_fd = wasip1::path_open(dir_fd, 0, filename, 0, wasip1::RIGHTS_FD_READ, 0, 0) - .expect("opening a file for writing"); - let res = wasip1::fd_write( + let file_fd = wasip1::path_open( + dir_fd, + 0, + filename, + 0, + wasip1::RIGHTS_FD_READ, + 0, + blocking_mode.fd_flags(), + ) + .expect("opening a file for writing"); + let res = blocking_mode.write( file_fd, &[wasip1::Ciovec { buf: 3 as *const u8, @@ -131,5 +150,8 @@ fn main() { }; // Run the tests. - unsafe { test_file_long_write(dir_fd, "long_write.txt") } + unsafe { + test_file_long_write(dir_fd, "long_write.txt", BlockingMode::Blocking); + test_file_long_write(dir_fd, "long_write.txt", BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/bin/p1_path_open_lots.rs b/crates/test-programs/src/bin/p1_path_open_lots.rs index 5b56d3b64357..6fe0d3b73ad5 100644 --- a/crates/test-programs/src/bin/p1_path_open_lots.rs +++ b/crates/test-programs/src/bin/p1_path_open_lots.rs @@ -1,29 +1,47 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::{create_file, open_scratch_directory}; +use test_programs::preview1::{BlockingMode, create_file, open_scratch_directory}; -unsafe fn test_path_open_lots(dir_fd: wasip1::Fd) { +unsafe fn test_path_open_lots(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { create_file(dir_fd, "file"); for _ in 0..2000 { - let f_readonly = wasip1::path_open(dir_fd, 0, "file", 0, wasip1::RIGHTS_FD_READ, 0, 0) - .expect("open file readonly"); + let f_readonly = wasip1::path_open( + dir_fd, + 0, + "file", + 0, + wasip1::RIGHTS_FD_READ, + 0, + blocking_mode.fd_flags(), + ) + .expect("open file readonly"); let buffer = &mut [0u8; 100]; let iovec = wasip1::Iovec { buf: buffer.as_mut_ptr(), buf_len: buffer.len(), }; - let nread = wasip1::fd_read(f_readonly, &[iovec]).expect("reading readonly file"); + let nread = blocking_mode + .read(f_readonly, &[iovec]) + .expect("reading readonly file"); assert_eq!(nread, 0, "readonly file is empty"); wasip1::fd_close(f_readonly).expect("close readonly"); } for _ in 0..2000 { - let f_readonly = wasip1::path_open(dir_fd, 0, "file", 0, wasip1::RIGHTS_FD_READ, 0, 0) - .expect("open file readonly"); + let f_readonly = wasip1::path_open( + dir_fd, + 0, + "file", + 0, + wasip1::RIGHTS_FD_READ, + 0, + blocking_mode.fd_flags(), + ) + .expect("open file readonly"); let buffer = &mut [0u8; 100]; let iovec = wasip1::Iovec { @@ -44,7 +62,7 @@ unsafe fn test_path_open_lots(dir_fd: wasip1::Fd) { 0, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .unwrap(); @@ -53,7 +71,7 @@ unsafe fn test_path_open_lots(dir_fd: wasip1::Fd) { buf: buffer.as_ptr(), buf_len: buffer.len(), }; - let nwritten = wasip1::fd_write(f, &[ciovec]).expect("write failed"); + let nwritten = blocking_mode.write(f, &[ciovec]).expect("write failed"); assert_eq!(nwritten, 100); wasip1::fd_close(f).unwrap(); @@ -67,7 +85,7 @@ unsafe fn test_path_open_lots(dir_fd: wasip1::Fd) { 0, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .unwrap(); @@ -105,5 +123,8 @@ fn main() { }; // Run the tests. - unsafe { test_path_open_lots(dir_fd) } + unsafe { + test_path_open_lots(dir_fd, BlockingMode::Blocking); + test_path_open_lots(dir_fd, BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/bin/p1_path_open_read_write.rs b/crates/test-programs/src/bin/p1_path_open_read_write.rs index 00d839878a7e..f91b874419c8 100644 --- a/crates/test-programs/src/bin/p1_path_open_read_write.rs +++ b/crates/test-programs/src/bin/p1_path_open_read_write.rs @@ -1,13 +1,21 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, process}; -use test_programs::preview1::{assert_errno, create_file, open_scratch_directory}; +use test_programs::preview1::{BlockingMode, assert_errno, create_file, open_scratch_directory}; -unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { +unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { create_file(dir_fd, "file"); - let f_readonly = wasip1::path_open(dir_fd, 0, "file", 0, wasip1::RIGHTS_FD_READ, 0, 0) - .expect("open file readonly"); + let f_readonly = wasip1::path_open( + dir_fd, + 0, + "file", + 0, + wasip1::RIGHTS_FD_READ, + 0, + blocking_mode.fd_flags(), + ) + .expect("open file readonly"); let stat = wasip1::fd_fdstat_get(f_readonly).expect("get fdstat readonly"); assert!( @@ -24,7 +32,9 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { buf: buffer.as_mut_ptr(), buf_len: buffer.len(), }; - let nread = wasip1::fd_read(f_readonly, &[iovec]).expect("reading readonly file"); + let nread = blocking_mode + .read(f_readonly, &[iovec]) + .expect("reading readonly file"); assert_eq!(nread, 0, "readonly file is empty"); let write_buffer = &[1u8; 50]; @@ -36,7 +46,8 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { // fails on windows with BADF, so we can't use the `windows =>` syntax // because that doesn't support alternatives like the agnostic syntax does. assert_errno!( - wasip1::fd_write(f_readonly, &[ciovec]) + blocking_mode + .write(f_readonly, &[ciovec]) .err() .expect("read of writeonly fails"), wasip1::ERRNO_PERM, @@ -46,8 +57,16 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { wasip1::fd_close(f_readonly).expect("close readonly"); // =============== WRITE ONLY ================== - let f_writeonly = wasip1::path_open(dir_fd, 0, "file", 0, wasip1::RIGHTS_FD_WRITE, 0, 0) - .expect("open file writeonly"); + let f_writeonly = wasip1::path_open( + dir_fd, + 0, + "file", + 0, + wasip1::RIGHTS_FD_WRITE, + 0, + blocking_mode.fd_flags(), + ) + .expect("open file writeonly"); let stat = wasip1::fd_fdstat_get(f_writeonly).expect("get fdstat writeonly"); assert!( @@ -65,13 +84,16 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { // See above for description of PERM assert_errno!( - wasip1::fd_read(f_writeonly, &[iovec]) + blocking_mode + .read(f_writeonly, &[iovec]) .err() .expect("read of writeonly fails"), wasip1::ERRNO_PERM, wasip1::ERRNO_BADF ); - let bytes_written = wasip1::fd_write(f_writeonly, &[ciovec]).expect("write to writeonly"); + let bytes_written = blocking_mode + .write(f_writeonly, &[ciovec]) + .expect("write to writeonly"); assert_eq!(bytes_written, write_buffer.len()); wasip1::fd_close(f_writeonly).expect("close writeonly"); @@ -85,7 +107,7 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { 0, wasip1::RIGHTS_FD_READ | wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("open file readwrite"); let stat = wasip1::fd_fdstat_get(f_readwrite).expect("get fdstat readwrite"); @@ -98,7 +120,9 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { "readwrite has write right" ); - let nread = wasip1::fd_read(f_readwrite, &[iovec]).expect("reading readwrite file"); + let nread = blocking_mode + .read(f_readwrite, &[iovec]) + .expect("reading readwrite file"); assert_eq!( nread, write_buffer.len(), @@ -110,7 +134,9 @@ unsafe fn test_path_open_read_write(dir_fd: wasip1::Fd) { buf: write_buffer_2.as_ptr(), buf_len: write_buffer_2.len(), }; - let bytes_written = wasip1::fd_write(f_readwrite, &[ciovec]).expect("write to readwrite"); + let bytes_written = blocking_mode + .write(f_readwrite, &[ciovec]) + .expect("write to readwrite"); assert_eq!(bytes_written, write_buffer_2.len()); let filestat = wasip1::fd_filestat_get(f_readwrite).expect("get filestat readwrite"); @@ -145,5 +171,8 @@ fn main() { }; // Run the tests. - unsafe { test_path_open_read_write(dir_fd) } + unsafe { + test_path_open_read_write(dir_fd, BlockingMode::Blocking); + test_path_open_read_write(dir_fd, BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/bin/p1_poll_oneoff_files.rs b/crates/test-programs/src/bin/p1_poll_oneoff_files.rs index 252ad1b92c49..0586c5e8d92c 100644 --- a/crates/test-programs/src/bin/p1_poll_oneoff_files.rs +++ b/crates/test-programs/src/bin/p1_poll_oneoff_files.rs @@ -1,7 +1,7 @@ #![expect(unsafe_op_in_unsafe_fn, reason = "old code, not worth updating yet")] use std::{env, mem::MaybeUninit, process}; -use test_programs::preview1::{assert_errno, open_scratch_directory}; +use test_programs::preview1::{BlockingMode, assert_errno, open_scratch_directory}; const CLOCK_ID: wasip1::Userdata = 0x0123_45678; @@ -189,7 +189,7 @@ unsafe fn test_fd_readwrite( ); } -unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasip1::Fd) { +unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { // Create a file in the scratch directory. let nonempty_file = wasip1::path_open( dir_fd, @@ -198,7 +198,7 @@ unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasip1::Fd) { wasip1::OFLAGS_CREAT, wasip1::RIGHTS_FD_WRITE, 0, - 0, + blocking_mode.fd_flags(), ) .expect("create writable file"); // Write to file @@ -207,7 +207,9 @@ unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasip1::Fd) { buf: contents.as_ptr() as *const _, buf_len: contents.len(), }; - wasip1::fd_write(nonempty_file, &[ciovec]).expect("write"); + blocking_mode + .write(nonempty_file, &[ciovec]) + .expect("write"); wasip1::fd_close(nonempty_file).expect("close"); // Now open the file for reading @@ -271,11 +273,11 @@ unsafe fn test_fd_readwrite_invalid_fd() { assert_eq!(err, wasip1::ERRNO_BADF) } -unsafe fn test_poll_oneoff(dir_fd: wasip1::Fd) { +unsafe fn test_poll_oneoff(dir_fd: wasip1::Fd, blocking_mode: BlockingMode) { test_timeout(); test_sleep(); test_empty_poll(); - test_fd_readwrite_valid_fd(dir_fd); + test_fd_readwrite_valid_fd(dir_fd, blocking_mode); test_fd_readwrite_invalid_fd(); } fn main() { @@ -298,5 +300,8 @@ fn main() { }; // Run the tests. - unsafe { test_poll_oneoff(dir_fd) } + unsafe { + test_poll_oneoff(dir_fd, BlockingMode::Blocking); + test_poll_oneoff(dir_fd, BlockingMode::NonBlocking); + } } diff --git a/crates/test-programs/src/preview1.rs b/crates/test-programs/src/preview1.rs index 4d4da78be3a1..afd7c868671e 100644 --- a/crates/test-programs/src/preview1.rs +++ b/crates/test-programs/src/preview1.rs @@ -1,4 +1,4 @@ -use std::{sync::OnceLock, time::Duration}; +use std::{mem::MaybeUninit, sync::OnceLock, time::Duration}; pub fn config() -> &'static TestConfig { static TESTCONFIG: OnceLock = OnceLock::new(); @@ -197,3 +197,72 @@ impl TestConfig { self.rename_dir_onto_file } } + +pub enum BlockingMode { + Blocking, + NonBlocking, +} + +impl BlockingMode { + pub fn fd_flags(&self) -> u16 { + match self { + BlockingMode::Blocking => 0, + BlockingMode::NonBlocking => wasip1::FDFLAGS_NONBLOCK, + } + } + + fn poll(fd: wasip1::Fd, event: wasip1::Eventtype) -> Result<(), wasip1::Errno> { + assert!( + unsafe { + wasip1::poll_oneoff( + [wasip1::Subscription { + userdata: 0, + u: wasip1::SubscriptionU { + tag: event.raw(), + u: wasip1::SubscriptionUU { + fd_read: wasip1::SubscriptionFdReadwrite { + file_descriptor: fd, + }, + }, + }, + }] + .as_ptr(), + MaybeUninit::::uninit().as_mut_ptr(), + 1, + ) + }? == 1 + ); + + Ok(()) + } + + pub unsafe fn read( + &self, + fd: wasip1::Fd, + iovs: wasip1::IovecArray<'_>, + ) -> Result { + loop { + match (self, unsafe { wasip1::fd_read(fd, iovs) }) { + (BlockingMode::NonBlocking, Err(wasip1::ERRNO_AGAIN)) => { + Self::poll(fd, wasip1::EVENTTYPE_FD_READ)?; + } + (_, result) => break result, + } + } + } + + pub unsafe fn write( + &self, + fd: wasip1::Fd, + iovs: wasip1::CiovecArray<'_>, + ) -> Result { + loop { + match (self, unsafe { wasip1::fd_write(fd, iovs) }) { + (BlockingMode::NonBlocking, Err(wasip1::ERRNO_AGAIN)) => { + Self::poll(fd, wasip1::EVENTTYPE_FD_WRITE)?; + } + (_, result) => break result, + } + } + } +} diff --git a/crates/wasi-common/src/sync/dir.rs b/crates/wasi-common/src/sync/dir.rs index cba660e7e4a1..5ad37441cba6 100644 --- a/crates/wasi-common/src/sync/dir.rs +++ b/crates/wasi-common/src/sync/dir.rs @@ -95,7 +95,13 @@ impl Dir { } else { // NONBLOCK does not have an OpenOption either, but we can patch that on with set_fd_flags: if fdflags.contains(crate::file::FdFlags::NONBLOCK) { - let set_fd_flags = f.new_set_fd_flags(system_interface::fs::FdFlags::NONBLOCK)?; + let set_fd_flags = f.new_set_fd_flags( + if fdflags.contains(crate::file::FdFlags::APPEND) { + system_interface::fs::FdFlags::APPEND + } else { + system_interface::fs::FdFlags::empty() + } | system_interface::fs::FdFlags::NONBLOCK, + )?; f.set_fd_flags(set_fd_flags)?; } Ok(OpenResult::File(File::from_cap_std(f))) diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index 3e89ea191da0..7235c1f24d37 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -1271,20 +1271,11 @@ pub unsafe extern "C" fn fd_read( let read_len = u64::try_from(len).trapping_unwrap(); let wasi_stream = streams.get_read_stream()?; - let data = match state - .with_one_import_alloc(ptr, len, || blocking_mode.read(wasi_stream, read_len)) - { - Ok(data) => data, - Err(streams::StreamError::Closed) => { - *nread = 0; - return Ok(()); - } - Err(streams::StreamError::LastOperationFailed(e)) => { - Err(stream_error_to_errno(e))? - } - }; + let data = state.with_one_import_alloc(ptr, len, || { + blocking_mode.read(wasi_stream, read_len) + })?; - assert_eq!(data.as_ptr(), ptr); + assert!(data.is_empty() || data.as_ptr() == ptr); assert!(data.len() <= len); // If this is a file, keep the current-position pointer up to date. @@ -1294,8 +1285,7 @@ pub unsafe extern "C" fn fd_read( .set(file.position.get() + data.len() as filesystem::Filesize); } - let len = data.len(); - *nread = len; + *nread = data.len(); forget(data); Ok(()) } @@ -2563,14 +2553,22 @@ impl BlockingMode { // note: these methods must take self, not &self, to avoid rustc creating a constant // out of a BlockingMode literal that it places in .romem, creating a data section and // breaking our fragile linking scheme - fn read( - self, - input_stream: &streams::InputStream, - read_len: u64, - ) -> Result, streams::StreamError> { + fn read(self, input_stream: &streams::InputStream, read_len: u64) -> Result, Errno> { match self { - BlockingMode::NonBlocking => input_stream.read(read_len), - BlockingMode::Blocking => input_stream.blocking_read(read_len), + BlockingMode::Blocking => match input_stream.blocking_read(read_len) { + Ok(data) => Ok(data), + Err(streams::StreamError::Closed) => Ok(Vec::new()), + Err(streams::StreamError::LastOperationFailed(e)) => Err(stream_error_to_errno(e)), + }, + BlockingMode::NonBlocking => match input_stream.read(read_len) { + Ok(data) if data.is_empty() && read_len > 0 => { + forget(data); + Err(ERRNO_AGAIN) + } + Ok(data) => Ok(data), + Err(streams::StreamError::Closed) => Ok(Vec::new()), + Err(streams::StreamError::LastOperationFailed(e)) => Err(stream_error_to_errno(e)), + }, } } fn write( @@ -2599,6 +2597,7 @@ impl BlockingMode { BlockingMode::NonBlocking => { let permit = match output_stream.check_write() { + Ok(0) if bytes.len() > 0 => return Err(ERRNO_AGAIN), Ok(n) => n, Err(streams::StreamError::Closed) => 0, Err(streams::StreamError::LastOperationFailed(e)) => {