Skip to content

Instantly share code, notes, and snippets.

@stormslowly
Created July 17, 2026 13:29
Show Gist options
  • Select an option

  • Save stormslowly/b44c6b1540bc70a98a2aeaa8695e13a9 to your computer and use it in GitHub Desktop.

Select an option

Save stormslowly/b44c6b1540bc70a98a2aeaa8695e13a9 to your computer and use it in GitHub Desktop.
Repro: rspack NativeWatcher concurrent &mut race crash (<= 2.1.4, macOS FSEvents use-after-free)
// Repro for a concurrent `&mut` race in NativeWatcher (rspack <= 2.1.4).
//
// Usage:
// npm i @rspack/binding@2.1.4
// node repro-native-watcher-crash.js
//
// On macOS the process dies within a few iterations, typically with:
// NSInvalidArgumentException: -[OS_os_log addObject:]: unrecognized selector
// (use-after-free on notify's FSEvents watched-paths CFMutableArray).
// Debug builds instead hit tokio's `!curr.is_join_waker_set()` assertion.
const fs = require("node:fs");
const os = require("node:os");
const path = require("node:path");
const binding = require("@rspack/binding");
const ITERATIONS = 30;
const WATCHES_PER_ITERATION = 5;
const FILE_COUNT = 400;
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rspack-native-watcher-"));
const files = [];
const dirs = [dir];
for (let i = 0; i < FILE_COUNT; i++) {
const sub = path.join(dir, `sub${i % 20}`);
fs.mkdirSync(sub, { recursive: true });
const file = path.join(sub, `f${i}.js`);
fs.writeFileSync(file, `module.exports = ${i};`);
files.push(file);
if (!dirs.includes(sub)) dirs.push(sub);
}
const startWatch = (watcher) => {
watcher.watch(
[files, []],
[dirs, []],
[[], []],
BigInt(Date.now()),
() => {},
() => {},
);
};
(async () => {
console.log(`pid: ${process.pid}`);
for (let i = 0; i < ITERATIONS; i++) {
const watcher = new binding.NativeWatcher({ aggregateTimeout: 1 });
for (let j = 0; j < WATCHES_PER_ITERATION; j++) startWatch(watcher);
await watcher.close();
try {
startWatch(watcher);
} catch (e) {
/* expected: has been closed */
}
console.log(`iteration ${i + 1}/${ITERATIONS} survived`);
}
console.log("ALL ITERATIONS SURVIVED (no crash)");
fs.rmSync(dir, { recursive: true, force: true });
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment