Skip to content

Commit 6fc9709

Browse files
committed
stream: fix Writable.toWeb() hang on synchronous drain
A race condition in the Writable.toWeb() adapter caused the stream to hang if the underlying Node.js Writable emitted a 'drain' event synchronously during a write() call. This often happened when highWaterMark was set to 0. By checking writableNeedDrain immediately after a backpressured write, the adapter now correctly detects if the stream has already drained, resolving the backpressure promise instead of waiting indefinitely for an event that has already occurred. Fixes: #61145
1 parent 9289ad5 commit 6fc9709

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

lib/internal/webstreams/adapters.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ function newWritableStreamFromStreamWritable(streamWritable, options = kEmptyObj
238238
options[kValidateChunk]?.(chunk);
239239
if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) {
240240
backpressurePromise = PromiseWithResolvers();
241+
if (!streamWritable.writableNeedDrain) {
242+
backpressurePromise.resolve();
243+
}
241244
return SafePromisePrototypeFinally(
242245
backpressurePromise.promise, () => {
243246
backpressurePromise = undefined;

test/parallel/test-whatwg-webstreams-adapters-to-writablestream.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,3 +197,26 @@ class TestWritable extends Writable {
197197
assert.strictEqual(chunk, arrayBuffer);
198198
}));
199199
}
200+
201+
{
202+
// Test that the stream doesn't hang when the underlying Writable
203+
// emits 'drain' synchronously during write().
204+
// Fixes: https://github.com/nodejs/node/issues/61145
205+
const writable = new Writable({
206+
write(chunk, encoding, callback) {
207+
callback();
208+
},
209+
});
210+
211+
// Force synchronous 'drain' emission during write()
212+
// to simulate a stream that doesn't have Node.js's built-in kSync protection.
213+
writable.write = function(chunk) {
214+
this.emit('drain');
215+
return false;
216+
};
217+
218+
const writableStream = newWritableStreamFromStreamWritable(writable);
219+
const writer = writableStream.getWriter();
220+
writer.write(new Uint8Array([1, 2, 3])).then(common.mustCall());
221+
writer.write(new Uint8Array([4, 5, 6])).then(common.mustCall());
222+
}

0 commit comments

Comments
 (0)