Repository navigation
Block on pending writes in close - #845
Merged
alexcrichton merged 2 commits intoJul 21, 2026
Merged
Conversation
This commit updates the `close` function to block on pending socket writes if they are present. This was already done for UDP `send`s but I forgot to do this for TCP/files/etc. This is effectively the decision reached in WebAssembly/component-model#617. This is a bit of a curious case as well because the blocking behavior in `close` is only needed when writes are buffered by wasi-libc, and writes are only buffered when a stream is deduced as not respecting 0-length operations for readiness. The test case here is written with TCP, and TCP is the poster-child for "should respect 0-length operation", so the entire premise of this commit may be a bit suspect. The underlying cause, however, is how Tokio and certain interleavings of `poll` work together. Tokio, Wasmtime's runtime for TCP, will report a TCP stream are ready for writes with no syscalls performed until some operation actually hits `EWOULDBLOCK`. This means that performing a zero-length operation is a noop until something else actually blocks. Normally this is fine, but it means that wasi-libc can deduce that Wasmtime's TCP streams are misbehaving via: * A socket is filled such that one more call to `write` will block, but otherwise hasn't blocked yet. * A call to `poll` is made which does a zero-length write. Tokio's never seen `EWOULDBLOCK` so it says that the stream is immediately ready. * The next call to `write` thinks that the previous zero-length write should force readiness, but now Tokio discoveres for the first time that the socket isn't ready, blocking the write. This confuses wasi-libc into thinking that writes must be buffered now. This can probably be fixed with enhanced logic in wasi-libc, but it's also orthogonal to the actual fix here. The actual fix is needed no matter what, blocking on reported-successful writes. A future optimization in wasi-libc might mean that it doesn't so aggressively start buffering writes, but that's left for later. For now the test included here exercises this path, and while it might stop in the future that's hopefully not a huge issue.
pchickey
approved these changes
Jul 21, 2026
Collaborator
|
I wonder (as someone who is continuously confronted with my own ignorance of POSIX despite using it for decades) how this overlaps with SO_LINGER. |
Collaborator
Author
|
Unfortunately I don't think that has much intersection with this, this is a libc-buffered write which was reported successful followed by a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This commit updates the
closefunction to block on pending socket writes if they are present. This was already done for UDPsends but I forgot to do this for TCP/files/etc. This is effectively the decision reached in WebAssembly/component-model#617.This is a bit of a curious case as well because the blocking behavior in
closeis only needed when writes are buffered by wasi-libc, and writes are only buffered when a stream is deduced as not respecting 0-length operations for readiness. The test case here is written with TCP, and TCP is the poster-child for "should respect 0-length operation", so the entire premise of this commit may be a bit suspect.The underlying cause, however, is how Tokio and certain interleavings of
pollwork together. Tokio, Wasmtime's runtime for TCP, will report a TCP stream are ready for writes with no syscalls performed until some operation actually hitsEWOULDBLOCK. This means that performing a zero-length operation is a noop until something else actually blocks. Normally this is fine, but it means that wasi-libc can deduce that Wasmtime's TCP streams are misbehaving via:writewill block, but otherwise hasn't blocked yet.pollis made which does a zero-length write. Tokio's never seenEWOULDBLOCKso it says that the stream is immediately ready.writethinks that the previous zero-length write should force readiness, but now Tokio discoveres for the first time that the socket isn't ready, blocking the write. This confuses wasi-libc into thinking that writes must be buffered now.This can probably be fixed with enhanced logic in wasi-libc, but it's also orthogonal to the actual fix here. The actual fix is needed no matter what, blocking on reported-successful writes. A future optimization in wasi-libc might mean that it doesn't so aggressively start buffering writes, but that's left for later. For now the test included here exercises this path, and while it might stop in the future that's hopefully not a huge issue.