Skip to content

Commit f58e8eb

Browse files
ronagTrott
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 0b495a8 commit f58e8eb

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
@@ -2063,7 +2063,9 @@ when `_read()` is called again after it has stopped should it resume pushing
20632063
additional data onto the queue.
20642064

20652065
Once the `readable._read()` method has been called, it will not be called again
2066-
until the [`readable.push()`][stream-push] method is called.
2066+
until more data is pushed through the [`readable.push()`][stream-push] method.
2067+
Empty data such as empty buffers and strings will not cause `readable._read()`
2068+
to be called.
20672069

20682070
The `size` argument is advisory. For implementations where a "read" is a
20692071
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)