mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 10:15:07 +08:00
vfs: fix promises.watch() with an already-aborted signal
fs.promises.watch() over a virtual mount resolved the first next()
with { done: true } when the signal was already aborted, while native
fs.promises.watch() rejects with ABORT_ERR.
Buffer the AbortError in VFSWatchAsyncIterable so the first next()
rejects once and later calls behave like an exhausted iterator,
matching the native async generator. Concurrently pending next()
calls now resolve after the rejecting one instead of all rejecting.
Signed-off-by: Kamat, Trivikram <16024985+trivikr@users.noreply.github.com>
Assisted-by: opencode
PR-URL: https://github.com/nodejs/node/pull/66254
Fixes: https://github.com/nodejs/node/issues/66253
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
1 parent
44d5d57ad2
commit
fcb1aa83f6
2 files changed
+20
-3
No files matched your search
@@ -4,6 +4,7 @@ const {
|
||||
ArrayPrototypePush,
|
||||
ObjectAssign,
|
||||
Promise,
|
||||
PromiseReject,
|
||||
PromiseResolve,
|
||||
SafeMap,
|
||||
SafeSet,
|
||||
@@ -579,6 +580,7 @@ const kMaxPendingEvents = 1024;
|
||||
class VFSWatchAsyncIterable {
|
||||
#watcher;
|
||||
#closed = false;
|
||||
#abortError = null;
|
||||
#pendingEvents = [];
|
||||
#pendingResolvers = [];
|
||||
|
||||
@@ -616,14 +618,21 @@ class VFSWatchAsyncIterable {
|
||||
}
|
||||
});
|
||||
|
||||
// Handle abort signal - reject pending next() with AbortError
|
||||
// Handle abort signal - reject the next pending next() with AbortError,
|
||||
// then behave like an exhausted iterator, matching native fs.promises.watch.
|
||||
if (signal) {
|
||||
const onAbort = () => {
|
||||
this.#closed = true;
|
||||
const err = new AbortError(undefined, { cause: signal.reason });
|
||||
while (this.#pendingResolvers.length > 0) {
|
||||
if (this.#pendingResolvers.length > 0) {
|
||||
const { reject } = this.#pendingResolvers.shift();
|
||||
reject(err);
|
||||
while (this.#pendingResolvers.length > 0) {
|
||||
const { resolve } = this.#pendingResolvers.shift();
|
||||
resolve({ done: true, value: undefined });
|
||||
}
|
||||
} else {
|
||||
this.#abortError = err;
|
||||
}
|
||||
this.#watcher.close();
|
||||
};
|
||||
@@ -648,6 +657,12 @@ class VFSWatchAsyncIterable {
|
||||
* @returns {Promise<IteratorResult>}
|
||||
*/
|
||||
next() {
|
||||
if (this.#abortError !== null) {
|
||||
const err = this.#abortError;
|
||||
this.#abortError = null;
|
||||
return PromiseReject(err);
|
||||
}
|
||||
|
||||
if (this.#closed) {
|
||||
return PromiseResolve({ done: true, value: undefined });
|
||||
}
|
||||
|
||||
@@ -28,11 +28,13 @@ const vfs = require('node:vfs');
|
||||
setImmediate(() => myVfs.writeFileSync('/file.txt', 'b'));
|
||||
}
|
||||
|
||||
// promises.watch with pre-aborted signal resolves done immediately
|
||||
// promises.watch with pre-aborted signal rejects the first next() with
|
||||
// AbortError, matching native fs.promises.watch
|
||||
{
|
||||
const myVfs = vfs.create();
|
||||
myVfs.writeFileSync('/p.txt', 'a');
|
||||
const iter = myVfs.promises.watch('/p.txt', { signal: AbortSignal.abort() });
|
||||
await assert.rejects(iter.next(), { name: 'AbortError', code: 'ABORT_ERR' });
|
||||
const r = await iter.next();
|
||||
assert.strictEqual(r.done, true);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user