fix(fsevent): stop the run loop via a CFRunLoopSource to avoid a shutdown race - #7
Merged
Conversation
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#816) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#817) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#821) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#822) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#826) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…rs#828) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#829) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#830) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#831) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#833) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#836) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#838) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#839) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#840) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* feat: get watched paths * Add changelog entry
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* perf: Optimize debouncer.watch() lookup time Use sorted VecDeque on 30k invocations cuts down all the linear scanning work that has be done before. Cuts down 10s to 300ms * fix: preserve recursive root lookup with sorted watch roots * add changelog entry --------- Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
…-rs#916) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…tify-rs#923) * fix(windows): emit Remove event when watched directory is deleted ReadDirectoryChangesW returns ERROR_ACCESS_DENIED when the watched directory's handle is invalidated. The existing handler unwatches but does not emit any event (PR notify-rs#674), leaving consumers with a dead watch and no way to react. Bring Windows in line with inotify (PR notify-rs#540) and FSEvents, which both emit a Remove event when their watched root disappears. Adds a delete_self_dir test to each backend's test module covering the cross-platform contract. * prefer `try_exists` --------- Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…tify-rs#924) * perf(notify): avoid filesystem walks for recursive kqueue unwatch * Correct the changelog entry place
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…-rs#932) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* feat(notify): make FSEvents stream latency configurable Adds Config::with_fsevent_latency to control the latency passed to FSEventStreamCreate. Defaults to Duration::ZERO, preserving current behavior. Set at construction time only. * docs: add changelog entry --------- Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
* chore(deps): bump kqueue to 1.2.0 This version adds a number of fixes to major problems, specifically in that it eliminates panics in failure conditions and substantially increases performance of watch removals. * update Cargo.lock --------- Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
* add test * windows: report watch error correctly
…y-rs#933) * feat(notify-debouncer-full): use condvar for no idle CPU usage The existing code spawned a thread that looped forever and slept for one tick. This is valid, but results in some idle CPU usage. This adds a condvar that means that the debouncer loop will go back to sleep until such time as there are actually events to handle, eliminating the idle CPU usage. * refactor * add changelog entry --------- Co-authored-by: Yuki Okushi <huyuumi.dev@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
…down race `CFRunLoopStop` only takes effect while the target run loop is actually running. The FSEvents watcher thread publishes its run-loop handle and then enters `CFRunLoopRun`, so a concurrent `stop()` could call `CFRunLoopStop` in the window before the loop starts. That stop request is lost, the thread parks in `CFRunLoopRun` forever, and the `join()` in `stop()` deadlocks. Under heavy event load the loop may also never reach a "waiting" state, so approaches that wait for that are unreliable too. Replace the atomic stop flag plus direct `CFRunLoopStop` with a custom `CFRunLoopSource`. The source is created and added to the watcher thread's run loop before its handles are published, so the caller can never signal a source that isn't registered yet. `stop()` signals the source and wakes the loop; the source's perform callback then calls `CFRunLoopStop` from inside the run-loop thread, where it cannot be lost. Because source signals stay pending until processed, a stop that arrives before `CFRunLoopRun` is entered still stops the loop once it starts. Adds a deterministic test that a pre-run stop signal still stops the loop, and a rapid watch/unwatch stress test that would previously hang.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings
zed-industries/notifyup to currentnotify-rs/notifymainand adds our FSEvents run-loop shutdown fix on top. The same fix commit will be proposed upstream separately.The fix
CFRunLoopStoponly takes effect while the target run loop is actually running. The FSEvents watcher thread publishes its run-loop handle and then entersCFRunLoopRun, so a concurrentstop()can callCFRunLoopStopin the window before the loop starts. That stop is lost, the thread parks forever, and thejoin()instop()deadlocks. Under heavy event load the loop may also never reach a "waiting" state, so waiting on that is unreliable too.This replaces the atomic stop flag plus direct
CFRunLoopStopwith a customCFRunLoopSource, created and added to the watcher thread's run loop before its handles are published.stop()signals the source and wakes the loop; the source's perform callback then callsCFRunLoopStopfrom inside the run-loop thread, where it cannot be lost. Because source signals stay pending until processed, a stop that arrives beforeCFRunLoopRunis entered still stops the loop once it starts.Tests
Adds a deterministic test that a pre-run stop signal still stops the loop, and a rapid watch/unwatch stress test that previously hung. All
fseventtests pass, and clippy reports no new warnings versus upstream.