unix: Write to UV_HANDLE_BLOCKING_WRITES streams in the thread pool #4976
+217
−35
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.
On Unix, if a
uv_stream_thasUV_HANDLE_BLOCKING_WRITESset, either because we were unable to put the file inO_NONBLOCKmode when the stream was initialized, or because the stream has been shared with a subprocess viaUV_INHERIT_STREAM,uv_writecan block.As discussed in #3602, the nicest solution would be to use platform-specific features to do non-blocking writes to files regardless of the
O_NONBLOCKstate, but a fallback is needed regardless. This change implements the fallback by doing all writes to blocking streams in the thread pool.There are a handful of specific changes I'd like to get feedback on:
O_NONBLOCKon libuv-owned streams shared usingUV_INHERIT_STREAM, but not on files shared withUV_INHERIT_FD. We setUV_HANDLE_BLOCKING_WRITESonly when we put the file in blocking mode, sinceuv_stream_set_blockingis documented to makeuv_writeblock.UV_HANDLE_*enum is getting crowded. I putUV_HANDLE_WRITE_PENDINGon top of a Windows-only flag, but the name is generic enough that we might one day want to use it for Windows too. Should it go somewhere else?uv_fs_t* blocked_writeis a new field onUV_STREAM_PRIVATE_FIELDSfor now, breaking ABI.Handling
uv_closeis more complicated when we dispatch writes to the thread pool. We can try touv_cancelthe write request, but there are few portable things we can do to cancel an in-progress write once it has been picked up from the work queue, save for reserving a signal for libuv's use only. I implemented a compromise: we tryuv_cancel, but keep the FD open and defer calling the write callbacks until thewrite(2)call completes (the write request itself doesn't need to be completed fully). This seems acceptable because until #4966 the callback can't know how many bytes were written ifstatus == UV_ECANCELED.The added test reproduces a problem we encounter frequently in Julia, where spawning a process puts our stream into blocking mode, then a later write fills the pipe/TTY buffer and deadlocks.