Skip to content

Commit 6531b98

Browse files
committed
stream: fix multiple Writable.destroy() calls
Calling Writable.destroy() multiple times in the same tick could cause an assertion error. Fixes: #38189 PR-URL: #38221 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Nitzan Uziely <linkgoron@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 8d4936d commit 6531b98

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

lib/internal/streams/writable.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,11 @@ ObjectDefineProperties(Writable.prototype, {
770770
const destroy = destroyImpl.destroy;
771771
Writable.prototype.destroy = function(err, cb) {
772772
const state = this._writableState;
773-
if (!state.destroyed) {
773+
774+
// Invoke pending callbacks.
775+
if (!state.destroyed &&
776+
(state.bufferedIndex < state.buffered.length ||
777+
state[kOnFinished].length)) {
774778
process.nextTick(errorBuffer, state, new ERR_STREAM_DESTROYED('write'));
775779
}
776780
destroy.call(this, err, cb);

test/parallel/test-stream-writable-destroy.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,57 @@ const assert = require('assert');
417417
}));
418418
write.write('asd');
419419
}
420+
421+
{
422+
const ac = new AbortController();
423+
const write = addAbortSignal(ac.signal, new Writable({
424+
write(chunk, enc, cb) { cb(); }
425+
}));
426+
427+
write.on('error', common.mustCall((e) => {
428+
assert.strictEqual(e.name, 'AbortError');
429+
assert.strictEqual(write.destroyed, true);
430+
}));
431+
write.write('asd');
432+
ac.abort();
433+
}
434+
435+
{
436+
const ac = new AbortController();
437+
const write = new Writable({
438+
signal: ac.signal,
439+
write(chunk, enc, cb) { cb(); }
440+
});
441+
442+
write.on('error', common.mustCall((e) => {
443+
assert.strictEqual(e.name, 'AbortError');
444+
assert.strictEqual(write.destroyed, true);
445+
}));
446+
write.write('asd');
447+
ac.abort();
448+
}
449+
450+
{
451+
const signal = AbortSignal.abort();
452+
453+
const write = new Writable({
454+
signal,
455+
write(chunk, enc, cb) { cb(); }
456+
});
457+
458+
write.on('error', common.mustCall((e) => {
459+
assert.strictEqual(e.name, 'AbortError');
460+
assert.strictEqual(write.destroyed, true);
461+
}));
462+
}
463+
464+
{
465+
// Destroy twice
466+
const write = new Writable({
467+
write(chunk, enc, cb) { cb(); }
468+
});
469+
470+
write.end(common.mustCall());
471+
write.destroy();
472+
write.destroy();
473+
}

0 commit comments

Comments
 (0)