Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix file writes in preview 2 implementation #7394

Merged
merged 2 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions crates/test-programs/src/bin/preview1_file_write.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::{env, process};
use test_programs::preview1::open_scratch_directory;

unsafe fn test_file_long_write(dir_fd: wasi::Fd, filename: &str) {
// Open a file for writing
let file_fd = wasi::path_open(
dir_fd,
0,
filename,
wasi::OFLAGS_CREAT,
wasi::RIGHTS_FD_WRITE,
0,
0,
)
.expect("creating a file for writing");

let mut content = Vec::new();
// 16 byte string, 4096 times, is 64k
for n in 0..4096 {
let chunk = format!("123456789 {n:05} ");
assert_eq!(chunk.as_str().as_bytes().len(), 16);
content.extend_from_slice(chunk.as_str().as_bytes());
}

// Write to the file
let nwritten = wasi::fd_write(
file_fd,
&[wasi::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 = wasi::fd_filestat_get(file_fd).expect("reading file stats");
assert_eq!(
stat.size,
content.len() as u64,
"file should be size of content",
);

wasi::fd_close(file_fd).expect("closing the file");
// Open the file for reading
let file_fd = wasi::path_open(dir_fd, 0, filename, 0, wasi::RIGHTS_FD_READ, 0, 0)
.expect("open the file for reading");

// Read the file's contents
let buffer = &mut [0u8; 100];
let nread = wasi::fd_read(
file_fd,
&[wasi::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!(
buffer,
&content[..buffer.len()],
"contents of first read chunk"
);

let end_cursor = content.len() - buffer.len();
wasi::fd_seek(file_fd, end_cursor as i64, wasi::WHENCE_SET)
.expect("seeking to end of file minus buffer size");

let nread = wasi::fd_read(
file_fd,
&[wasi::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");

wasi::fd_close(file_fd).expect("closing the file");
}

fn main() {
let mut args = env::args();
let prog = args.next().unwrap();
let arg = if let Some(arg) = args.next() {
arg
} else {
eprintln!("usage: {} <scratch directory>", prog);
process::exit(1);
};

// Open scratch directory
let dir_fd = match open_scratch_directory(&arg) {
Ok(dir_fd) => dir_fd,
Err(err) => {
eprintln!("{}", err);
process::exit(1)
}
};

// Run the tests.
unsafe { test_file_long_write(dir_fd, "long_write.txt") }
}
4 changes: 4 additions & 0 deletions crates/wasi-common/tests/all/async_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,7 @@ async fn preview1_path_open_preopen() {
async fn preview1_unicode_output() {
run(PREVIEW1_UNICODE_OUTPUT, true).await.unwrap()
}
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn preview1_file_write() {
run(PREVIEW1_FILE_WRITE, true).await.unwrap()
}
4 changes: 4 additions & 0 deletions crates/wasi-common/tests/all/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,3 +273,7 @@ fn preview1_path_open_preopen() {
fn preview1_unicode_output() {
run(PREVIEW1_UNICODE_OUTPUT, true).unwrap()
}
#[test_log::test]
fn preview1_file_write() {
run(PREVIEW1_FILE_WRITE, true).unwrap()
}
17 changes: 13 additions & 4 deletions crates/wasi/src/preview2/filesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ enum OutputState {
Ready,
/// Allows join future to be awaited in a cancellable manner. Gone variant indicates
/// no task is currently outstanding.
Waiting(AbortOnDropJoinHandle<io::Result<()>>),
Waiting(AbortOnDropJoinHandle<io::Result<usize>>),
/// The last I/O operation failed with this error.
Error(io::Error),
Closed,
Expand Down Expand Up @@ -233,22 +233,26 @@ impl HostOutputStream for FileOutputStream {
let m = self.mode;
let task = spawn_blocking(move || match m {
FileOutputMode::Position(mut p) => {
let mut total = 0;
let mut buf = buf;
while !buf.is_empty() {
let nwritten = f.write_at(buf.as_ref(), p)?;
// afterwards buf contains [nwritten, len):
let _ = buf.split_to(nwritten);
p += nwritten as u64;
total += nwritten;
}
Ok(())
Ok(total)
}
FileOutputMode::Append => {
let mut total = 0;
let mut buf = buf;
while !buf.is_empty() {
let nwritten = f.append(buf.as_ref())?;
let _ = buf.split_to(nwritten);
total += nwritten;
}
Ok(())
Ok(total)
}
});
self.state = OutputState::Waiting(task);
Expand Down Expand Up @@ -285,7 +289,12 @@ impl Subscribe for FileOutputStream {
async fn ready(&mut self) {
if let OutputState::Waiting(task) = &mut self.state {
self.state = match task.await {
Ok(()) => OutputState::Ready,
Ok(nwritten) => {
if let FileOutputMode::Position(ref mut p) = &mut self.mode {
*p += nwritten as u64;
}
OutputState::Ready
}
Err(e) => OutputState::Error(e),
};
}
Expand Down
4 changes: 4 additions & 0 deletions crates/wasi/tests/all/async_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ async fn preview1_path_open_preopen() {
async fn preview1_unicode_output() {
run(PREVIEW1_UNICODE_OUTPUT_COMPONENT, true).await.unwrap()
}
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn preview1_file_write() {
run(PREVIEW1_FILE_WRITE_COMPONENT, false).await.unwrap()
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn preview2_sleep() {
Expand Down
4 changes: 4 additions & 0 deletions crates/wasi/tests/all/preview1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,7 @@ async fn preview1_path_open_preopen() {
async fn preview1_unicode_output() {
run(PREVIEW1_UNICODE_OUTPUT, true).await.unwrap()
}
#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn preview1_file_write() {
run(PREVIEW1_FILE_WRITE, true).await.unwrap()
}
4 changes: 4 additions & 0 deletions crates/wasi/tests/all/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ fn preview1_path_open_preopen() {
fn preview1_unicode_output() {
run(PREVIEW1_UNICODE_OUTPUT_COMPONENT, true).unwrap()
}
#[test_log::test]
fn preview1_file_write() {
run(PREVIEW1_FILE_WRITE_COMPONENT, false).unwrap()
}

#[test_log::test]
fn preview2_sleep() {
Expand Down