Skip to content

Commit 4b377e5

Browse files
committed
fs: fix nonNativeWatcher leak of StatWatchers
PR-URL: nodejs#45501 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 68cca1e commit 4b377e5

File tree

2 files changed

+30
-6
lines changed

2 files changed

+30
-6
lines changed

lib/internal/fs/recursive_watch.js

+3-6
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,11 @@ class FSWatcher extends EventEmitter {
154154
this.#symbolicFiles.add(f);
155155
}
156156

157+
this.#files.set(f, file);
157158
if (file.isFile()) {
158159
this.#watchFile(f);
159-
} else {
160-
this.#files.set(f, file);
161-
162-
if (file.isDirectory() && !file.isSymbolicLink()) {
163-
await this.#watchFolder(f);
164-
}
160+
} else if (file.isDirectory() && !file.isSymbolicLink()) {
161+
await this.#watchFolder(f);
165162
}
166163
}
167164
}

test/parallel/test-fs-watch-recursive.js

+27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const common = require('../common');
4+
const { setTimeout } = require('timers/promises');
45

56

67
const assert = require('assert');
@@ -52,3 +53,29 @@ if (common.isOSX) {
5253
process.on('exit', function() {
5354
assert(watcherClosed, 'watcher Object was not closed');
5455
});
56+
57+
(async () => {
58+
// Assert recursive watch does not leak handles
59+
const rootDirectory = fs.mkdtempSync(testDir + path.sep);
60+
const testDirectory = path.join(rootDirectory, 'test-7');
61+
const filePath = path.join(testDirectory, 'only-file.txt');
62+
fs.mkdirSync(testDirectory);
63+
64+
let watcherClosed = false;
65+
const watcher = fs.watch(testDirectory, { recursive: true });
66+
watcher.on('change', common.mustCallAtLeast(async (event, filename) => {
67+
await setTimeout(common.platformTimeout(100));
68+
if (filename === path.basename(filePath)) {
69+
watcher.close();
70+
watcherClosed = true;
71+
}
72+
await setTimeout(common.platformTimeout(100));
73+
assert(!process._getActiveHandles().some((handle) => handle.constructor.name === 'StatWatcher'));
74+
}));
75+
76+
process.on('exit', function() {
77+
assert(watcherClosed, 'watcher Object was not closed');
78+
});
79+
await setTimeout(common.platformTimeout(100));
80+
fs.writeFileSync(filePath, 'content');
81+
})().then(common.mustCall());

0 commit comments

Comments
 (0)