Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stream: reset awaitDrain after manual .resume() #7160

Closed
wants to merge 1 commit into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Jun 5, 2016

Checklist
  • tests and code linting passes
  • a test and/or benchmark is included
  • the commit message follows commit guidelines
Affected core subsystem(s)

stream

Description of change

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

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 awaitDrain mechanism worked like it does today.

/cc @nodejs/streams

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
@addaleax addaleax added the stream Issues and PRs related to the stream subsystem. label Jun 5, 2016
@addaleax
Copy link
Member Author

addaleax commented Jun 5, 2016

@mcollina
Copy link
Member

mcollina commented Jun 6, 2016

I think this should be ok to backport, but we should probably check CIGTM before that. cc @calvinmetcalf

LGTM

@jasnell
Copy link
Member

jasnell commented Jun 6, 2016

LGTM if @nodejs/streams folks are happy.

@addaleax
Copy link
Member Author

@thealphanerd since you self-assigned this… anything in particular you’d like to see before landing?

@calvinmetcalf
Copy link
Contributor

Doesn't look to be anything to controversial from my perspective

@MylesBorins
Copy link
Contributor

@calvinmetcalf I'm assuming you did not mean to close this 😄

@MylesBorins
Copy link
Contributor

@addaleax Nothing I need to see before landing, assigned to myself to keep an eye on it for LTS

@calvinmetcalf
Copy link
Contributor

@thealphanerd you are correct

sorry foks

@mafintosh
Copy link
Member

as long as @mcollina is happy i'm happy so 👍

addaleax added a commit that referenced this pull request Jun 13, 2016
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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@addaleax
Copy link
Member Author

Landed in e2e615e, thanks for the reviews and comments, everyone!

@addaleax addaleax closed this Jun 13, 2016
@addaleax addaleax deleted the fix-7159 branch June 13, 2016 21:58
evanlucas pushed a commit that referenced this pull request Jun 16, 2016
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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@evanlucas evanlucas mentioned this pull request Jun 16, 2016
abresas added a commit to balena-io-modules/open-balena-base that referenced this pull request Jun 20, 2016
abresas added a commit to balena-io-modules/open-balena-base that referenced this pull request Jun 20, 2016
MylesBorins pushed a commit that referenced this pull request Jun 23, 2016
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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Jun 24, 2016
MylesBorins pushed a commit that referenced this pull request Jun 24, 2016
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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Jun 24, 2016
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 <[email protected]>
Reviewed-By: James M Snell <[email protected]>
MylesBorins pushed a commit that referenced this pull request Jun 28, 2016
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
MylesBorins pushed a commit that referenced this pull request Jun 28, 2016
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
@MylesBorins MylesBorins removed their assignment Dec 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants