Skip to content

Commit 92d4e9d

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 05d6b9b commit 92d4e9d

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
@@ -215,6 +215,9 @@ function newWritableStreamFromStreamWritable(streamWritable) {
215215
write(chunk) {
216216
if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) {
217217
backpressurePromise = PromiseWithResolvers();
218+
if (!streamWritable.writableNeedDrain) {
219+
backpressurePromise.resolve();
220+
}
218221
return SafePromisePrototypeFinally(
219222
backpressurePromise.promise, () => {
220223
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
@@ -165,3 +165,26 @@ class TestWritable extends Writable {
165165
const writer = writableStream.getWriter();
166166
writer.closed.then(common.mustCall());
167167
}
168+
169+
{
170+
// Test that the stream doesn't hang when the underlying Writable
171+
// emits 'drain' synchronously during write().
172+
// Fixes: https://github.com/nodejs/node/issues/61145
173+
const writable = new Writable({
174+
write(chunk, encoding, callback) {
175+
callback();
176+
},
177+
});
178+
179+
// Force synchronous 'drain' emission during write()
180+
// to simulate a stream that doesn't have Node.js's built-in kSync protection.
181+
writable.write = function(chunk) {
182+
this.emit('drain');
183+
return false;
184+
};
185+
186+
const writableStream = newWritableStreamFromStreamWritable(writable);
187+
const writer = writableStream.getWriter();
188+
writer.write(new Uint8Array([1, 2, 3])).then(common.mustCall());
189+
writer.write(new Uint8Array([4, 5, 6])).then(common.mustCall());
190+
}

0 commit comments

Comments
 (0)