Skip to content

Commit 6c75cc1

Browse files
ronagBridgeAR
authored andcommitted
stream: do not deadlock duplexpair
If nothing is buffered then _read will not be called and the callback will not be invoked, effectivly deadlocking. Fixes: #29758 PR-URL: #29836 Refs: #29649 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
1 parent e7f604f commit 6c75cc1

File tree

3 files changed

+15
-5
lines changed

3 files changed

+15
-5
lines changed

doc/api/stream.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -2059,7 +2059,9 @@ when `_read()` is called again after it has stopped should it resume pushing
20592059
additional data onto the queue.
20602060

20612061
Once the `readable._read()` method has been called, it will not be called again
2062-
until the [`readable.push()`][stream-push] method is called.
2062+
until more data is pushed through the [`readable.push()`][stream-push] method.
2063+
Empty data such as empty buffers and strings will not cause `readable._read()`
2064+
to be called.
20632065

20642066
The `size` argument is advisory. For implementations where a "read" is a
20652067
single operation that returns data can use the `size` argument to determine how

lib/internal/streams/duplexpair.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ class DuplexSocket extends Duplex {
2020
}
2121

2222
_write(chunk, encoding, callback) {
23-
this[kOtherSide][kCallback] = callback;
24-
this[kOtherSide].push(chunk);
23+
if (chunk.length === 0) {
24+
process.nextTick(callback);
25+
} else {
26+
this[kOtherSide].push(chunk);
27+
this[kOtherSide][kCallback] = callback;
28+
}
2529
}
2630

2731
_final(callback) {

test/common/duplexpair.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ class DuplexSocket extends Duplex {
2424
_write(chunk, encoding, callback) {
2525
assert.notStrictEqual(this[kOtherSide], null);
2626
assert.strictEqual(this[kOtherSide][kCallback], null);
27-
this[kOtherSide][kCallback] = callback;
28-
this[kOtherSide].push(chunk);
27+
if (chunk.length === 0) {
28+
process.nextTick(callback);
29+
} else {
30+
this[kOtherSide].push(chunk);
31+
this[kOtherSide][kCallback] = callback;
32+
}
2933
}
3034

3135
_final(callback) {

0 commit comments

Comments
 (0)