stream: reset awaitDrain after manual .resume()#7160
stream: reset awaitDrain after manual .resume()#7160addaleax wants to merge 1 commit intonodejs:masterfrom
Conversation
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: nodejs#7159
|
I think this should be ok to backport, but we should probably check CIGTM before that. cc @calvinmetcalf LGTM |
|
LGTM if @nodejs/streams folks are happy. |
|
@thealphanerd since you self-assigned this… anything in particular you’d like to see before landing? |
|
Doesn't look to be anything to controversial from my perspective |
|
@calvinmetcalf I'm assuming you did not mean to close this 😄 |
|
@addaleax Nothing I need to see before landing, assigned to myself to keep an eye on it for LTS |
|
@thealphanerd you are correct sorry foks |
|
as long as @mcollina is happy i'm happy so 👍 |
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: #7159 PR-URL: #7160 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
|
Landed in e2e615e, thanks for the reviews and comments, everyone! |
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: #7159 PR-URL: #7160 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
fixes streams error nodejs/node#7160
fixes streams error nodejs/node#7160
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: #7159 PR-URL: #7160 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: #7159 PR-URL: #7160 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Reset the `readableState.awaitDrain` counter after manual calls to `.resume()`. What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario: 1. The writable stream indicates that its buffer is full. 2. This leads the readable stream to `pause()` and increase its `awaitDrain` counter, which will be decreased by the writable’s next `drain` event. 3. Something calls `.resume()` manually. 4. The readable continues to pipe to the writable, but once again the writable stream indicates that the buffer is full. 5. The `awaitDrain` counter is thus increased again, but since it has now been increased twice for a single piping destination, the next `drain` event will not be able to reset `awaitDrain` to zero. 6. The pipe is stalled and no data is passed along anymore. The solution in this commit is to reset the `awaitDrain` counter to zero when `resume()` is called. Fixes: #7159 PR-URL: #7160 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This LTS release comes with 89 commits. This includes 46 commits that
are docs related, 11 commits that are test related, 8 commits that are
build related, and 4 commits that are benchmark related.
Notable Changes:
- debugger:
- All properties of an array (aside from length) can now be printed
in the repl (cjihrig)
#6448
- npm:
- Upgrade npm to 2.15.8 (Rebecca Turner)
#7412
- stream:
- Fix for a bug that became more prevalent with the stream changes
that landed in v4.4.5. (Anna Henningsen)
#7160
- V8:
- Fix for a bug in crankshaft that was causing crashes on arm64
(Myles Borins)
#7442
- Add missing classes to postmortem info such as JSMap and JSSet
(evan.lucas)
#3792
This LTS release comes with 89 commits. This includes 46 commits that
are docs related, 11 commits that are test related, 8 commits that are
build related, and 4 commits that are benchmark related.
Notable Changes:
- debugger:
- All properties of an array (aside from length) can now be printed
in the repl (cjihrig)
#6448
- npm:
- Upgrade npm to 2.15.8 (Rebecca Turner)
#7412
- stream:
- Fix for a bug that became more prevalent with the stream changes
that landed in v4.4.5. (Anna Henningsen)
#7160
- V8:
- Fix for a bug in crankshaft that was causing crashes on arm64
(Myles Borins)
#7442
- Add missing classes to postmortem info such as JSMap and JSSet
(evan.lucas)
#3792
johnaAr555
left a comment
There was a problem hiding this comment.
var state = this._readableState;
if (!state.flowing) {
debug('resume');
johnaAr555
left a comment
There was a problem hiding this comment.
var state = this._readableState;
if (!state.flowing) {
debug('resume');
Checklist
Affected core subsystem(s)
stream
Description of change
Reset the
readableState.awaitDraincounter after manual calls to.resume().What might happen otherwise is that a slow consumer at the end of the pipe could end up stalling the piping in the following scenario:
pause()and increase itsawaitDraincounter, which will be decreased by the writable’s nextdrainevent..resume()manually.awaitDraincounter is thus increased again, but since it has now been increased twice for a single piping destination, the nextdrainevent will not be able to resetawaitDrainto zero.The solution in this commit is to reset the
awaitDraincounter to zero whenresume()is called.Fixes: #7159
This bug is has been around at least since 0.12, and I don’t currently know why it never popped up before #2325 when the
awaitDrainmechanism worked like it does today./cc @nodejs/streams