Fix FSEvents routing for differently cased watch paths - #64191
Fix FSEvents routing for differently cased watch paths#64191Jake Bailey (jakebailey) wants to merge 5 commits into
Conversation
Use the watched volume's case sensitivity when routing FSEvents and filtering file watches. Preserve caller-visible root casing, including shared callbacks, overflow matching, and root deletion handling.
Skip shared path prefixes eight bytes at a time and handle ASCII case
folding without component splitting. Retain the Unicode fallback for
case-equivalent paths with different UTF-8 lengths.
Reuse the parent directory's comparer for WatchFile instead of querying
filesystem case sensitivity twice. Add routing benchmarks and expand
boundary and Unicode alignment coverage.
Compare against the initial casing fix on Apple M1. Allocations are
unchanged: zero for exact matches and rejections, and one for rebasing
a differently cased event path.
goos: darwin
goarch: arm64
pkg: github.com/microsoft/TypeScript/tsc/internal/fswatch
cpu: Apple M1
│ before │ after │
│ sec/op │ sec/op vs base │
FSEventsDisplayPath/exact-match-8 9.031n ± ∞ ¹ 8.958n ± ∞ ¹ ~ (p=0.548 n=5)
FSEventsDisplayPath/case-mismatch-8 130.50n ± ∞ ¹ 80.89n ± ∞ ¹ -38.02% (p=0.008 n=5)
FSEventsDisplayPath/sibling-miss-8 103.20n ± ∞ ¹ 14.26n ± ∞ ¹ -86.18% (p=0.008 n=5)
FSEventsDisplayPath/unrelated-miss-8 26.120n ± ∞ ¹ 6.676n ± ∞ ¹ -74.44% (p=0.008 n=5)
FSEventsDisplayPath/unicode-match-8 115.30n ± ∞ ¹ 74.29n ± ∞ ¹ -35.57% (p=0.008 n=5)
FSEventsDisplayPath/unicode-length-match-8 98.83n ± ∞ ¹ 52.34n ± ∞ ¹ -47.04% (p=0.008 n=5)
FSEventsRoutingFanout/100-8 10.799µ ± ∞ ¹ 1.634µ ± ∞ ¹ -84.87% (p=0.008 n=5)
FSEventsRoutingFanout/1000-8 109.21µ ± ∞ ¹ 15.10µ ± ∞ ¹ -86.18% (p=0.008 n=5)
geomean 284.3n 94.97n -66.60%
¹ need >= 6 samples for confidence interval at level 0.95
Use CoreFoundation case folding and NFC normalization for non-ASCII comparisons on case-insensitive Darwin watches. Recognize expanding aliases such as sharp s and SS without merging distinct dotted-I, dotless-I, circled-letter, or fullwidth spellings. Preserve the ASCII fast path, prepare watch-root comparison forms, and share lazy event folding across routing and callback filtering. Rebase using original path boundaries rather than folded byte lengths. Cover file and directory watches, shared callbacks, symlinks, overflow, and root termination with filesystem regressions and routing benchmarks. Keep fswatch isolated and leave Windows and case-sensitive behavior unchanged.
There was a problem hiding this comment.
🔵 Needs a closer look
Darwin-specific filesystem semantics, Unicode folding, and assembly-backed CoreFoundation integration warrant final human validation.
Pull request overview
Fixes FSEvents event routing when requested path casing differs from disk casing.
Changes:
- Adds volume-aware, Unicode-capable path comparison.
- Applies case-aware routing to directory, file, overflow, and deletion events.
- Adds macOS integration tests, unit tests, benchmarks, and documentation.
File summaries
| File | Description |
|---|---|
tsc/internal/fswatch/watcher.go |
Integrates comparers into watch sharing and callback routing. |
tsc/internal/fswatch/watcher_test.go |
Updates file callback tests. |
tsc/internal/fswatch/README.md |
Documents FSEvents casing behavior. |
tsc/internal/fswatch/pathcompare.go |
Implements path comparison, folding, and rebasing. |
tsc/internal/fswatch/pathcompare_test.go |
Tests comparison and file filtering. |
tsc/internal/fswatch/fsevents_darwin.go |
Adds case-aware FSEvents routing. |
tsc/internal/fswatch/fsevents_darwin_fold_test.go |
Tests Unicode folding and aliases. |
tsc/internal/fswatch/fsevents_darwin_ffi.s |
Adds the CoreFoundation folding trampoline. |
tsc/internal/fswatch/fsevents_darwin_ffi.go |
Implements native CoreFoundation path folding. |
tsc/internal/fswatch/fsevents_darwin_case_test.go |
Tests casing differences on macOS filesystems. |
tsc/internal/fswatch/fsevents_darwin_bench_test.go |
Benchmarks routing and folding fan-out. |
tsc/internal/fswatch/CHANGES.md |
Records the behavior change. |
tsc/internal/fswatch/canonicalize_other.go |
Provides non-Darwin comparer behavior. |
tsc/internal/fswatch/canonicalize_darwin.go |
Detects per-volume case sensitivity. |
Review details
- Files reviewed: 14/14 changed files
- Comments generated: 0
- Review effort level: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
|
This was a weekend Astra experiment; it does seem to fix everything. The scary thing I noticed however is that tspath's path casing (inherited from Strada) is actually... not correct for macOS? It makes me wonder if it's even correct for Windows either. For this, however, I kept |
|
I believe |
Enable the existing Darwin path comparer for kqueue subscriptions so WatchFile recognizes alternate casing, expanding Unicode aliases, and normalization-equivalent names reported by directory enumeration. Add shared Darwin coverage for creation, modification, deletion, and distinct filenames. Preserve kqueue directory-event spelling and leave case-sensitive comparison and other platforms unchanged.
Describe volume-aware matching for FSEvents and kqueue, native Unicode folding, backend-specific event spelling, and cached comparison forms. Clarify the scope of the APFS observations and unchanged platform behavior.
|
So, this fixes it for fswatch itself, but then we run into the problem that its callers also are interested in checking the paths, and they can get confused too, so this may not actually fix anything. I'm looking into anything that might help |
Fixes #64089