fix(p3-shim): report invalid read offsets through future - #1804
Conversation
This is attempt to fix bytecodealliance#1787
|
Hey @andreiltd thanks for the PR, this looks great -- I took a look at this too, and I ran into a couple other things that I thought were the issue instead:
Any reason we shouldn't do that? I'm not sure what case the stream aborting the errors was created for. Moving the check out certainly works as well -- happy to take that as a fix, but would like to know what the case is that we do want to abort the stream with the error.
There are two issues:
Right now what I've been prototyping locally looks like the following: async writeViaStream(data, offset) {
this.#ensureWritable(data);
const stream = readableByteStreamFromReader(data, { name: "file write data" });
try {
let release = await this.lockFDForOperation({ fd: this.#handle.fd, op: `write on fd [${this.#handle.fd}]` });
const work = worker().run({ op: "write", fd: this.#handle.fd, offset, stream }, [stream]);
while (true) {
const finished = await Promise.race([
new Promise((resolve) => setTimeout(() => resolve(null), 100)),
work,
]);
// if we got the worker finished, break out
if (finished !== null) {
break;
}
// If we didn't, release and re-acquire the FD operation
release()
release = await this.lockFDForOperation({ fd: this.#handle.fd, op: `write on fd [${this.#handle.fd}]` });
}
release();
} catch (err) {
throw FSError.from(err);
}
}( This prototype isn't ideal since it's just timeout based, but being able to treat worker operations as a generator (knowing when it has made some progress, or has yielded because it couldn't make any progress) and run them until completion (with some time to allow other operations to take place) along with some queueing should solve this problem in a much more satisfying way. Basically the idea is that we need a way to know that the write operation made some progress, and to allow the waiting While we can change the upstream tests, I think the code as written is not unreasonable so it might be a good idea to try to investigate avenues for supporting it, |
Hey, so my mental model for the difference between closing and aborting stream is:
but it looks like wasi wants both to happen: the stream closes with an https://github.com/WebAssembly/WASI/blob/main/proposals/filesystem/wit/types.wit#L321
Right, that seems like an issue, but maybe there is some lock free solution? For example creating a queue of operations on the worker side?
I think the fix in |
Yeah, so in this case the error is in the filesystem domain, so we'd theoretically want to throw the error here too, no? I think this might be pointing us to needing to envelope, or at least catch all errors on the bindgen side and return them as enveloped values.
Yeah whether the queue is on the invoking side or the worker side is fine with me, IFF you actually get to the worker side in time to queue properly. The problem is that That said we need both queueing and resumable operations.
Yeah I'm not saying changing the upstream code is wrong, it's certainly less racy to do it that way -- I'm saying that the code that worked should work, and exposes a weakness in the current implementation. I think wasmtime does a write-wait-yield here -- there are writes on both sides of the stat. So the first write finishes, but then blocks waiting for more to write. Jco code does the same -- but the problem is that we have no way to come back out of the write after the partial write succeeds to allow something else to run so we get stuck. The write task gets stuck waiting for stream read to supply it more data forever, and never gives other tasks a chance. Modeled as a queue, you'd have the same problem, just as the first task in the queue never yielding after making a little bit of progress. If we had a way to do some work then make progress, we could accept this kind of program. |
|
OK, so thinking about this some more, I think this boils down to a few things:
While I wanted to solve this by ordering the operations, I think adding that norm to |
This is attempt to fix #1787
I think to resolve #1787 we will need a fix on the wasi-testsuite side as well. The test is checking file size after each
stream.writecompleted and I think the check should be deferred to until after the stream future resolves.Specifically, I think this check is racy: https://github.com/WebAssembly/wasi-testsuite/blob/main/tests/rust/wasm32-wasip3/src/bin/filesystem-io.rs#L115
Edit: see WebAssembly/wasi-testsuite#278