Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/api/stream.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,15 @@ This property contains the number of bytes (or objects) in the queue
ready to be written. The value provides introspection data regarding
the status of the `highWaterMark`.

##### `writable.writableNeedDrain`
<!-- YAML
added: REPLACEME
-->

* {boolean}

Is `true` if buffer is full.
Comment thread
ronag marked this conversation as resolved.
Outdated

##### `writable.writableObjectMode`
<!-- YAML
added: v12.3.0
Expand Down
5 changes: 5 additions & 0 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,11 @@ ObjectDefineProperty(OutgoingMessage.prototype, 'writableEnded', {
get: function() { return this.finished; }
});

ObjectDefineProperty(OutgoingMessage.prototype, 'writableNeedDrain', {
get: function() {
return !this.destroyed && !this.finished && this[kNeedDrain];
}
});

const crlf_buf = Buffer.from('\r\n');
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {
Expand Down
2 changes: 2 additions & 0 deletions lib/internal/streams/duplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ ObjectDefineProperties(Duplex.prototype, {
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked'),
writableEnded:
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded'),
writableNeedDrain:
ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain'),

destroyed: {
get() {
Expand Down
4 changes: 4 additions & 0 deletions lib/internal/streams/pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ async function pump(iterable, writable, finish) {
}
let error;
try {
if (writable.writableNeedDrain === true) {
await EE.once(writable, 'drain');
Comment thread
ronag marked this conversation as resolved.
}

for await (const chunk of iterable) {
if (!writable.write(chunk)) {
if (writable.destroyed) return;
Expand Down
55 changes: 32 additions & 23 deletions lib/internal/streams/readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -727,35 +727,44 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
ondrain();
}

function pause() {
// If the user unpiped during `dest.write()`, it is possible
// to get stuck in a permanently paused state if that write
// also returned false.
// => Check whether `dest` is still a piping destination.
if (!cleanedUp) {
if (state.pipes.length === 1 && state.pipes[0] === dest) {
debug('false write response, pause', 0);
state.awaitDrainWriters = dest;
state.multiAwaitDrain = false;
} else if (state.pipes.length > 1 && state.pipes.includes(dest)) {
debug('false write response, pause', state.awaitDrainWriters.size);
state.awaitDrainWriters.add(dest);
}
src.pause();
}
if (!ondrain) {
// When the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
ondrain = pipeOnDrain(src, dest);
dest.on('drain', ondrain);
}
}

src.on('data', ondata);

if (dest.writableNeedDrain === true) {
Comment thread
ronag marked this conversation as resolved.
Outdated
pause();
}

function ondata(chunk) {
debug('ondata');
const ret = dest.write(chunk);
debug('dest.write', ret);
if (ret === false) {
// If the user unpiped during `dest.write()`, it is possible
// to get stuck in a permanently paused state if that write
// also returned false.
// => Check whether `dest` is still a piping destination.
if (!cleanedUp) {
if (state.pipes.length === 1 && state.pipes[0] === dest) {
debug('false write response, pause', 0);
state.awaitDrainWriters = dest;
state.multiAwaitDrain = false;
} else if (state.pipes.length > 1 && state.pipes.includes(dest)) {
debug('false write response, pause', state.awaitDrainWriters.size);
state.awaitDrainWriters.add(dest);
}
src.pause();
}
if (!ondrain) {
// When the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
ondrain = pipeOnDrain(src, dest);
dest.on('drain', ondrain);
}
pause();
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib/internal/streams/writable.js
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,14 @@ ObjectDefineProperties(Writable.prototype, {
}
},

writableNeedDrain: {
get() {
const wState = this._writableState;
if (!wState) return false;
return !this._writable.destroyed && !wState.ending && wState.needDrain;
}
},

writableHighWaterMark: {
get() {
return this._writableState && this._writableState.highWaterMark;
Expand Down