Repository: node
Issue: Not possible to know when fs.watch has started on macOS
Labels: fs,libuv,macos

### Version

v21.7.3

### Platform

Darwin Mac-1713547127951.local 21.6.0 Darwin Kernel Version 21.6.0: Mon Feb 19 20:24:34 PST 2024; root:xnu-8020.240.18.707.4~1/RELEASE_X86_64 x86_64

### Subsystem

fs

### What steps will reproduce the bug?

By experimentation I have determined that, on Windows and Linux, `fs.watch` is watching the filesystem as soon as the function call returns.

Unfortunately, on macOS, `fs.watch` does not start watching the filesystem until some indeterminate amount of time after the function call returns. It is not possible to know when this is.

Consider the following code:

```js
import {open} from "node:fs/promises";
import {watch} from "node:fs";
import {basename} from "node:path";

const path = "test";
const file = await open(path, "w");
await file.close();
const abortController = new AbortController();
const events = [];
const watcher = watch(
    path,
    {signal: abortController.signal},
    (event, path) => void events.push({event, path})
);
watcher.addListener("error", error => void events.push({event: "Error", error}));
// If you insert a delay here, then sometimes the test will pass on macOS.
// The longer the delay, the more likely the test is to pass.
const file2 = await open(path, "w");
await file2.close();
// You can insert a delay here if you want, but it doesn't seem to make any difference.
abortController.abort();
console.log(events);
```

### How often does it reproduce? Is there a required condition?

For me, on macOS on GitHub Actions, the output of the above code is always

```txt
[]
```

and on Windows or Linux on GitHub Actions, the output is always
```txt
[ { event: 'change', path: 'test' } ]
```

However, I would not be surprised if macOS occasionally produces the same output as Windows/Linux by random chance.

### What is the expected behavior? Why is that the expected behavior?

The expected output of the above script is:

```txt
[ { event: 'change', path: 'test' } ]
```

This is the actual output on Windows and Linux.

I would expect that most applications that use `fs.watch` do something like the following:

1. Call `fs.watch` to get a stream of events.
2. Read the initial state of the filesystem.
3. Re-read any files that are the subject of events emitted by `fs.watch`.

On Windows and Linux, which behave as I would expect, the application can always safely read the state of the filesystem (step 2) immediately after calling `fs.watch`. The application can be sure that any changes that occur after it reads the initial state of the filesystem will be included in the stream of events emitted by `fs.watch`. Therefore it is possible for the application to be reliably in sync with the actual state of the filesystem.

### What do you see instead?

On macOS, the output of the above script is:

```txt
[]
```

On macOS, the application cannot know when the stream of events from `fs.watch` will actually start. Therefore it cannot know when it is safe to read the initial state of the filesystem (step 2 in the above set of steps). If the application reads the initial state of the filesystem too early, then the application will be out of sync with the actual state of the filesystem after changes occur. The application could insert an arbitrary delay before step 2 (in my experimentation, 200ms seems to work well), but this degrades performance and is not a reliable solution.

### Additional information

The expected behaviour described above imply that `fs.watch` performs a certain amount of synchronous IO before it returns. It may not be possible or desirable to implement this behaviour on macOS.

Instead, I suggest that, on all platforms, `fs.watch` should emit a new "ready" event when it starts actively watching the filesystem. Applications can then wait until they observe this "ready" event before they read the initial state of the filesystem.