Repository navigation
Release the endpoints of a native TypeScript streams pipe - #8056
Conversation
|
💬 Approved with comments — The fix is correct and well tested. One optional suggestion from the last review is still open: a synchronous throw from the pipeFrom hook, after both streams are extracted, leaves them locked. Findings: 1 suggestion The decision is advisory: this comment never approves, blocks or requests changes on the pull request. 📋 Code Quality: 💬 1 suggestionThe fix is correct. One optional hardening suggestion from the last review is still open. Recommendation: 💬 Optional Nits
View full reviewA synchronous throw after extraction leaves both endpoints locked ( Both
The outer catch then rejects, and both endpoints stay locked for good. That is the outcome this pull request fixes on the asynchronous path. The dispatch checks make most of these throws unreachable, so this is defensive hardening, not a regression. To close the gap, turn a synchronous throw into a rejected promise and pass that promise to The new ✅ All other checks passed🔒 Security: ✅ LGTMNo security concerns. 📖 AGENTS.md: ✅ LGTMThe AGENTS.md updates match the new behaviour. ⚡ Performance: ✅ LGTMNo performance concerns. 📝 Documentation: ✅ LGTMNo documentation issues. Reviewed commit: |
|
LGTM! PR #8056 releases TypeScript native-pipe endpoint locks and expands socket/RPC coverage. Other findings
Specialist findings not posted (1)
Reviewed commit: 35f0c1fc · github run |
Under typescript_implemented_streams, pipeTo() from a native-backed readable (a fetch() or service-binding body, a buffer-backed body, Blob.stream(), a socket's readable) into a native-backed writable (a connect() socket's writable, a writable received over JS RPC) runs at the C++ layer through the sink's pipeFrom hook. Extracting the two native halves locks both streams with internal handles, which the fast path never released: once the pipe settled, both endpoints stayed locked for good, so the writable could not be closed, aborted or written again, and the source could not be read or cancelled. The JS pipe path, and the C++ implementation, release both. Once pipeFrom settles, the fast path now finishes the pipe as the JS pipe's shutdown and finalize would: it closes the destination through its internal writer when the pipe succeeded, or aborts it with the pipe's error, and then releases the writer and the source's reader. The extracted sink's hooks no longer reach the C++ sink, so neither reaches the connection. The source has been closed since its extraction and stays closed, so it reads done whatever the outcome. On an abort that is the spec's cancel. C++ instead errors the source when the pipe is aborted or the source fails, and with an already-aborted signal touches neither endpoint. The sockets suite pins these as a divergence. Tests: nativePipeReleasesEndpoints in the sockets suite (success, mid-pipe abort, an already-aborted signal, a failing service-binding body), and the cross-implementation RPC test for a writable received over RPC, with and without a write made before the pipe, and with a failing source. typescript_implemented_streams is still experimental, with no enable date, so the change needs no compatibility flag. Fixes: #8049
35f0c1f to
2eb5b37
Compare
Under typescript_implemented_streams, pipeTo() from a native-backed readable (a fetch() or service-binding body, a buffer-backed body, Blob.stream(), a socket's readable) into a native-backed writable (a connect() socket's writable, a writable received over JS RPC) runs at the C++ layer through the sink's pipeFrom hook. Extracting the two native halves locks both streams with internal handles, which the fast path never released: once the pipe settled, both endpoints stayed locked for good, so the writable could not be closed, aborted or written again, and the source could not be read or cancelled. The JS pipe path, and the C++ implementation, release both.
Once pipeFrom settles, the fast path now finishes the pipe as the JS pipe's shutdown and finalize would: it closes the destination through its internal writer when the pipe succeeded, or aborts it with the pipe's error, and then releases the writer and the source's reader. The extracted sink's hooks no longer reach the C++ sink, so neither reaches the connection.
The source has been closed since its extraction and stays closed, so it reads done whatever the outcome. On an abort that is the spec's cancel. C++ instead errors the source when the pipe is aborted or the source fails, and with an already-aborted signal touches neither endpoint. The sockets suite pins these as a divergence.
Tests: nativePipeReleasesEndpoints in the sockets suite (success, mid-pipe abort, an already-aborted signal, a failing service-binding body), and the cross-implementation RPC test for a writable received over RPC, with and without a write made before the pipe, and with a failing source.
typescript_implemented_streams is still experimental, with no enable date, so the change needs no compatibility flag.
Fixes: #8049