-
Notifications
You must be signed in to change notification settings - Fork 266
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 fdRead: handle io.Reader corner cases #740
Conversation
Signed-off-by: Nuno Cruces <ncruces@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the help. I would like to progress this with you.
The one case is very curious why we are ignoring the error when n != 0. I'd like to know what that error was when you can safely do this.
For example, it makes sense to ignore error when io.EOF, but why would we ignore any type of error in this case? can you give more info?
At the moment Test_fdWrite isn't a matrix, but if we don't test this it will likely become re-broken. Can you attempt to matrix this to include your corner cases?
wasi_snapshot_preview1/fs.go
Outdated
@@ -411,7 +411,12 @@ var fdRead = wasm.NewGoFunc( | |||
if errors.Is(err, io.EOF) { | |||
break | |||
} else if err != nil { | |||
if n != 0 { // Let callers process the n > 0 bytes returned before considering the error err. | |||
break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't this swallow the error? how can they consider the err if there's none returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLDR: they handle the error on their next call to readv
(fread
, etc).
If you assume the error is stable (e.g. an external disk was removed), once they readv
again they'll get the same error.
OTOH, if the error is transient (which is unlikely for real filesystems), once they call readv
again the error will be lost, but at least there is no data loss.
So yes, this may swallow errors in 2 situations:
- if
n
bytes are enough for the caller, and they don'treadv
again. - if
err
is transient, and isn't returned on a future call toreadv
.
Both seem legitimate reasons to ignore the error, rather than loosing/ignoring the data.
wasi_snapshot_preview1/fs.go
Outdated
return ErrnoIo | ||
} else if n < len(b) { // Partial read, don't read into the next buffer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cool
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this was the one that worried me the most.
I can look into tests but it might take me a while, as I'll be OOO for a couple of weeks. #739 was the issue that was blocking me, this was just something I found while debugging why reads were failing me, but it turned out not to be the issue. Here, the The other one (returning data and an error) probably doesn't happen much in the real world to begin with. |
@ncruces thanks for the background. I think some libraries will do their own sort of error mediation above the syscall layer wrt some read failure, though I understand that the read already occurred and a stream often can't be re-read. Mainly, it feels a little dodgy. If we do something that is a bit dodgy, what I would say is to cite something else (ex x/sys for some arch or some underlying impl). Meanwhile, we can add a comment about the possible issue and move on for now. Meanwhile, I'll help with the tests on this PR since you'll be out. I'll also look for any other place where we have a similar read loop. Mainly, I appreciate your attention, focus and research which is worth more to the project than having your personally do the test matrix stuff, especially as we plan to do our first beta in a couple weeks. TL;DR; is I'll push a commit to this branch and merge while you're gone. Have a nice one! |
What you could also do, since you can't return both the error and the data, is to do as |
well, we can write the data before returning the errno. that's fine |
we want to avoid too much logic and allocations at the syscall abstraction which is repeated at higher abstractions, so trying to do compensating buffering which may also occur in compiled wasm probably isn't the right choice, iotw |
Yes, but callers of |
sounds fine as I wasn't particularly interested in writing it on error anyway ;) |
@ncruces I didn't quite read your comment below correctly first time. My gut feel is storing extra state about an error might be problematic, but then again WebAssembly doesn't really implement concurrency, anyway, so it might not be so bad. Will think and decide tomorrow after playing around
|
Signed-off-by: Adrian Cole <adrian@tetrate.io>
backfilled docs, will do the test tomorrow |
I just read your rational, the explanation is perfect. You can ignore the rest of my comment, since I'm just restating your rational, basically.
PS: if I have a lot of spare time I may have a go at running (some form of) SQLite inside WASI. That should be a nice stress test for the existing and missing WASI APIs. And it should be fun to (somehow) compare SQLite on top of |
Signed-off-by: Adrian Cole <adrian@tetrate.io>
@mathetake PTAL! |
thanks again @ncruces and looking forward to your future developments |
These are to handle corner cases described in
io.Reader
.For a partial read
readv
really shouldn't continue reading into the following buffer.And according to
io.Reader
, callers should handle any read bytes before considering the error.