Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/internal/streams/fast-utf8-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,9 @@ class Utf8Stream extends EventEmitter {
// start
if ((!this.#writing && this.#len > this.#minLength) || this.#flushPending) {
this.#actualWrite();
} else if (reopening) {
} else if (reopening && !this.#writing) {
// Do not emit 'drain' if a 'ready' listener started a write:
// #release() will emit the real 'drain' when that write completes.
process.nextTick(() => this.emit('drain'));
}
};
Expand Down
42 changes: 42 additions & 0 deletions test/parallel/test-fastutf8stream-reopen.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const {
openSync,
readFile,
renameSync,
write,
} = require('node:fs');
const { Utf8Stream } = require('node:fs');
const { join } = require('node:path');
Expand All @@ -27,6 +28,47 @@ function getTempFile() {
runTests(false);
runTests(true);

// A write started by a 'ready' listener after reopen() must complete before
// 'drain' is emitted. Deferring the reopened file's write by one setImmediate
// makes the write land after the nextTick on which reopen() used to emit a
// premature 'drain'. Async mode only: sync mode writes before 'ready'.
{
const dest = getTempFile();
const after = dest + '-new';
const stream = new Utf8Stream({
dest,
minLength: 0,
sync: false,
fs: {
write(fd, buf, enc, cb) {
if (stream.file === after) {
setImmediate(() => write(fd, buf, enc, cb));
return;
}
return write(fd, buf, enc, cb);
},
},
});

assert.ok(stream.write('hello world\n'));

stream.once('drain', common.mustCall(() => {
stream.reopen(after);

stream.once('ready', common.mustCall(() => {
assert.ok(stream.write('after reopen\n'));

stream.once('drain', common.mustCall(() => {
assert.strictEqual(stream.writing, false);
readFile(after, 'utf8', common.mustSucceed((data) => {
assert.strictEqual(data, 'after reopen\n');
stream.end();
}));
}));
}));
}));
}

function runTests(sync) {

{
Expand Down
Loading