return ERRNO_AGAIN from fd_{read,write} when appropriate#13111
return ERRNO_AGAIN from fd_{read,write} when appropriate#13111alexcrichton merged 4 commits intobytecodealliance:mainfrom
ERRNO_AGAIN from fd_{read,write} when appropriate#13111Conversation
|
Could you detail a bit more about how this surfaced and came here? I'm effectively not certain this is the best way to fix the underlying issue. The interaction with One example is that nonblocking writes look pretty buggy on the surface:
And that may not be the full extent of possible issues here... |
|
The symptom that prompted this PR is that a Go WASIp1 module which attempts to read a file immediately gets what it thinks is an EOF instead of the content when wrapped in a WASIp2 (or WASIp3+WASIp2) component using the p1 adapter. When I investigated, I found that Go explicitly sets all file descriptors to non-blocking using So we either need to ignore the |
Aren't they supposed to return |
Nevermind, I see what you're pointing to now. Yes, I'll need to fix that, but it's fixable. |
The WASIp1 `fd_read` and `fd_write` functions should return `Err(ERRNO_AGAIN)` rather Ok(0) if a non-blocking operation fails to transfer any bytes. [The docs](https://github.com/WebAssembly/WASI/blob/wasi-0.1/preview1/docs.md) aren't particularly clear about this other than saying these functions are "similar to [readv,writev] in POSIX", but if any functions ought to ever return `ERRNO_AGAIN`, it's these. I'm guessing this hasn't been noticed (or at least not reported) until now because most guests use blocking I/O for files, but Go uses non-blocking I/O to support concurrent I/O across multiple goroutines, and it only knows to call `poll_oneoff` (rather than e.g. assume EOF) if it gets an `EAGAIN`.
540f3e3 to
10a9b0b
Compare
alexcrichton
left a comment
There was a problem hiding this comment.
I suppose in the sake of being conservative it's probably best to apply a fix like this (returning the right codes) instead of behaviorally changing by ignoring nonblocking flags. Another risk to consider here though is that the adapter may have different behavior than the native implementation of WASIp1, which is something we've historically avoided. Could you add a test which exercises this at a higher level? That test should automatically be run by the various implementations with-and-without the adapter which can help validate the behavior in each situation.
If we end up going this route (e.g. a test behaves well across implementations), could you update the read path to look more like the write path though? For example could the BlockingMode::read function return a result-with-wasi-errno instead of the StreamError it does today? That would move the handling of the 0 return value there to look simliar to the write path.
Reading from files via e.g. `os.ReadFile` was broken due to a bug in the `wasi_snapshot_preview1` adapter _and_ another bug in my patched version of Go. The following PR and commit fix those issues: - bytecodealliance/wasmtime#13111 - dicej/go@1f14282 I've updated the adapter using a build from bytecodealliance/wasmtime@d60f178 and pointed the Go URL to the new release URL. I've also updated the download directory to force a new download for users which may already have the old version. Finally, I've updated the Go wrapper to point to the `v0.3.2` release (which doesn't exist yet, but will soon). Previously, we were pointing to the `canary` release on the `main` branch, but it's annoying to switch back and forth when tagging releases, so let's just leave it set to whatever the latest release is for now.
Reading from files via e.g. `os.ReadFile` was broken due to a bug in the `wasi_snapshot_preview1` adapter _and_ another bug in my patched version of Go. The following PR and commit fix those issues: - bytecodealliance/wasmtime#13111 - dicej/go@1f14282 I've updated the adapter using a build from bytecodealliance/wasmtime@d60f178 and pointed the Go URL to the new release URL. I've also updated the download directory to force a new download for users which may already have the old version. Finally, I've updated the Go wrapper to point to the `v0.3.2` release (which doesn't exist yet, but will soon). Previously, we were pointing to the `canary` release on the `main` branch, but it's annoying to switch back and forth when tagging releases, so let's just leave it set to whatever the latest release is for now.
The WASIp1
fd_readandfd_writefunctions should returnErr(ERRNO_AGAIN)rather Ok(0) if a non-blocking operation fails to transfer any bytes. The docs aren't particularly clear about this other than saying these functions are "similar to [readv,writev] in POSIX", but if any functions ought to ever returnERRNO_AGAIN, it's these.I'm guessing this hasn't been noticed (or at least not reported) until now because most guests use blocking I/O for files, but Go uses non-blocking I/O to support concurrent I/O across multiple goroutines, and it only knows to call
poll_oneoff(rather than e.g. assume EOF) if it gets anEAGAIN.