stream: ensure finish is emitted in next tick

When using end() it was possible for 'finish' to
be emitted synchronously.

PR-URL: https://github.com/nodejs/node/pull/30733
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
Robert Nagy 2019-11-30 15:39:44 +01:00 committed by Rich Trott
parent 8dea6dc2fb
commit e13a37e49d
4 changed files with 44 additions and 17 deletions

View File

@ -698,11 +698,24 @@ function prefinish(stream, state) {
} }
} }
function finishMaybe(stream, state) { function finishMaybe(stream, state, sync) {
const need = needFinish(state); const need = needFinish(state);
if (need) { if (need) {
prefinish(stream, state); prefinish(stream, state);
if (state.pendingcb === 0) { if (state.pendingcb === 0) {
state.pendingcb++;
if (sync) {
process.nextTick(finish, stream, state);
} else {
finish(stream, state);
}
}
}
return need;
}
function finish(stream, state) {
state.pendingcb--;
state.finished = true; state.finished = true;
stream.emit('finish'); stream.emit('finish');
@ -715,13 +728,10 @@ function finishMaybe(stream, state) {
} }
} }
} }
}
return need;
}
function endWritable(stream, state, cb) { function endWritable(stream, state, cb) {
state.ending = true; state.ending = true;
finishMaybe(stream, state); finishMaybe(stream, state, true);
if (cb) { if (cb) {
if (state.finished) if (state.finished)
process.nextTick(cb); process.nextTick(cb);

View File

@ -70,5 +70,7 @@ const filename = path.join(tmpdir.path, 'sync-write-stream.txt');
assert.strictEqual(stream.fd, fd); assert.strictEqual(stream.fd, fd);
stream.end(); stream.end();
stream.on('close', common.mustCall(() => {
assert.strictEqual(stream.fd, null); assert.strictEqual(stream.fd, null);
}));
} }

View File

@ -238,13 +238,15 @@ const assert = require('assert');
// called again. // called again.
const write = new Writable({ const write = new Writable({
write: common.mustNotCall(), write: common.mustNotCall(),
final: common.mustCall((cb) => cb(), 2) final: common.mustCall((cb) => cb(), 2),
autoDestroy: true
}); });
write.end(); write.end();
write.destroy(); write.once('close', common.mustCall(() => {
write._undestroy(); write._undestroy();
write.end(); write.end();
}));
} }
{ {

View File

@ -28,3 +28,16 @@ const assert = require('assert');
assert.strictEqual(writable.writableFinished, true); assert.strictEqual(writable.writableFinished, true);
})); }));
} }
{
// Emit finish asynchronously
const w = new Writable({
write(chunk, encoding, cb) {
cb();
}
});
w.end();
w.on('finish', common.mustCall());
}