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
117 changes: 93 additions & 24 deletions lib/internal/fs/dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Dir {
return this.#path;
}

#processHandlerQueue() {
#processHandlerQueueSync() {
while (this.#handlerQueue.length > 0) {
const handler = ArrayPrototypeShift(this.#handlerQueue);
const { handle, path } = handler;
Expand All @@ -106,6 +106,89 @@ class Dir {
return this.#bufferedEntries.length > 0;
}

#processHandlerQueueAsync(maybeSync, callback) {
while (this.#handlerQueue.length > 0) {
const handler = ArrayPrototypeShift(this.#handlerQueue);
const { handle, path } = handler;

if (handler.pending === true) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: do this check before destructing { handle, path } = handler to avoid doing the destructing when the loop breaks early

ArrayPrototypePush(this.#handlerQueue, handler);
break;
}

handler.pending = true;
this.#operationQueue = [];

const req = new FSReqCallback();
req.oncomplete = (err, result) => {
process.nextTick(() => {
const queue = this.#operationQueue;
this.#operationQueue = null;
for (const op of queue) op();
});

handler.pending = false;

if (err) {
handle.close();
callback(err);
return;
}

if (result === null) {
handle.close();
this.#readImpl(maybeSync, callback);
return;
}

try {
this.#processReadResult(path, result);
if (result.length > 0) {
ArrayPrototypePush(this.#handlerQueue, handler);
} else {
handle.close();
}
this.#emitBufferedDirent(maybeSync, callback);
} catch (error) {
callback(error);
}
};

handle.read(
this.#options.encoding,
this.#options.bufferSize,
req,
);
return true;
}

return false;
}

#emitBufferedDirent(maybeSync, callback) {
if (this.#bufferedEntries.length === 0) {
return false;
}

try {
const dirent = ArrayPrototypeShift(this.#bufferedEntries);

if (this.#options.recursive && dirent.isDirectory()) {
this.#readSyncRecursive(dirent);
}

if (maybeSync) {
process.nextTick(callback, null, dirent);
} else {
callback(null, dirent);
}
} catch (error) {
callback(error);
}

return true;
}

read(callback) {
return arguments.length === 0 ? this.#readPromisified() : this.#readImpl(true, callback);
}
Expand All @@ -121,29 +204,19 @@ class Dir {

validateFunction(callback, 'callback');

if (this.#emitBufferedDirent(maybeSync, callback)) {
return;
}

if (this.#operationQueue !== null) {
ArrayPrototypePush(this.#operationQueue, () => {
this.#readImpl(maybeSync, callback);
});
return;
}

if (this.#processHandlerQueue()) {
try {
const dirent = ArrayPrototypeShift(this.#bufferedEntries);

if (this.#options.recursive && dirent.isDirectory()) {
this.#readSyncRecursive(dirent);
}

if (maybeSync)
process.nextTick(callback, null, dirent);
else
callback(null, dirent);
return;
} catch (error) {
return callback(error);
}
if (this.#processHandlerQueueAsync(maybeSync, callback)) {
return;
}

const req = new FSReqCallback();
Expand All @@ -160,11 +233,7 @@ class Dir {

try {
this.#processReadResult(this.#path, result);
const dirent = ArrayPrototypeShift(this.#bufferedEntries);
if (this.#options.recursive && dirent.isDirectory()) {
this.#readSyncRecursive(dirent);
}
callback(null, dirent);
this.#emitBufferedDirent(maybeSync, callback);
} catch (error) {
callback(error);
}
Expand Down Expand Up @@ -202,7 +271,7 @@ class Dir {
return;
}

ArrayPrototypePush(this.#handlerQueue, { handle, path });
ArrayPrototypePush(this.#handlerQueue, { handle, path, pending: false });
}

readSync() {
Expand All @@ -214,7 +283,7 @@ class Dir {
throw new ERR_DIR_CONCURRENT_OPERATION();
}

if (this.#processHandlerQueue()) {
if (this.#processHandlerQueueSync()) {
const dirent = ArrayPrototypeShift(this.#bufferedEntries);
if (this.#options.recursive && dirent.isDirectory()) {
this.#readSyncRecursive(dirent);
Expand Down
13 changes: 13 additions & 0 deletions test/sequential/test-fs-opendir-recursive.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,19 @@ function processDirCb(dir, cb) {
test().then(common.mustCall());
}

{
async function testBufferSize() {
const dir = await fsPromises.opendir(testDir, { recursive: true, bufferSize: 1 });
const dirents = [];
for await (const dirent of dir) {
dirents.push(dirent);
}
assertDirents(dirents);
}

testBufferSize().then(common.mustCall());
}

// Issue https://github.com/nodejs/node/issues/48820 highlights that
// opendir recursive does not properly handle the buffer size option.
// This test asserts that the buffer size option is respected.
Expand Down