Skip to content

Fix flaky PhysicalFilesWatcher timeout when root is deleted and recreated#130967

Draft
svick wants to merge 1 commit into
dotnet:mainfrom
svick:fix-flaky-filechangetoken-root-recreated
Draft

Fix flaky PhysicalFilesWatcher timeout when root is deleted and recreated#130967
svick wants to merge 1 commit into
dotnet:mainfrom
svick:fix-flaky-filechangetoken-root-recreated

Conversation

@svick

@svick svick commented Jul 17, 2026

Copy link
Copy Markdown
Member

Fixes #129691.

Problem

PhysicalFilesWatcherTests.CreateFileChangeToken_RootDeletedAndRecreated_TokenFiresWhenFileCreated intermittently fails with a 30s System.TimeoutException (the change token never fires), most frequently under jitstress/pgo legs on Linux.

Root cause

TryEnableFileSystemWatcher decided whether to set up the PendingCreationWatcher (the fallback that watches for the root directory to reappear) solely based on FileSystemWatcher.EnableRaisingEvents.

On Linux, when the watched root directory is deleted, the runtime FileSystemWatcher tears down the inotify watch and queues an Error, but does not reset EnableRaisingEvents — it keeps reporting true. The only thing that flips it to false is OnErrorTryDisableFileSystemWatcher.

The test deletes the root, then re-registers a token as soon as the initial token fires (from OnError's CancelAll). This is a race:

  • If the re-registration's TryEnableFileSystemWatcher runs after OnError's TryDisableFileSystemWatcher, EnableRaisingEvents is false and the PendingCreationWatcher is set up correctly.
  • If it runs before (which is what happens under load), EnableRaisingEvents is still true, so the if (!EnableRaisingEvents) block is skipped and no PendingCreationWatcher is created. TryDisableFileSystemWatcher then sees the freshly re-added token and does not disable. The result is a dead inotify watch bound to the deleted inode: recreating the root never re-arms it, and the token never fires.

Fix

In TryEnableFileSystemWatcher, handle the case where the watcher still reports enabled but _root no longer exists: tear down the stale watch (EnableRaisingEvents = false) and fall back to the root creation watcher. Since the test re-registers while the root is still deleted, this closes the race deterministically.

Note

This pull request was created by GitHub Copilot.

…ated

TryEnableFileSystemWatcher decided whether to set up the root creation
watcher solely based on FileSystemWatcher.EnableRaisingEvents. On Linux,
when the watched root directory is deleted the runtime FileSystemWatcher
tears down the inotify watch and queues an Error, but leaves
EnableRaisingEvents == true. It is only reset once OnError runs
TryDisableFileSystemWatcher.

If a token was re-registered before that happened, TryEnableFileSystemWatcher
saw EnableRaisingEvents == true and skipped setting up the PendingCreationWatcher,
leaving a dead inotify watch bound to the deleted inode. Recreating the root
never re-armed it, so the token never fired and the change token wait timed out
(30s), causing the flaky failure in
CreateFileChangeToken_RootDeletedAndRecreated_TokenFiresWhenFileCreated.

Handle the case where the watcher still reports enabled but the root no longer
exists by tearing down the stale watch and falling back to the root creation
watcher.

Fixes dotnet#129691

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: ea806a13-89ae-46ef-a4d9-03a6db0a506e
Copilot AI review requested due to automatic review settings July 17, 2026 13:03
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
12 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-filesystem
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens PhysicalFilesWatcher against a race where the underlying FileSystemWatcher can remain logically “enabled” while its OS-level watch has been torn down after the watched root directory is deleted (notably on Linux/inotify), causing subsequent re-registrations to miss root recreation and never observe the target file creation.

Changes:

  • In TryEnableFileSystemWatcher, detect the “enabled but root missing” state and proactively tear down the stale watch by setting EnableRaisingEvents = false.
  • Fall back to the existing root reappearance mechanism (PendingCreationWatcher) by setting needsRootWatcher = true and marking _rootWasUnavailable = true so the re-enable path performs the “gap” scan when the root comes back.

@svick

svick commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr libraries-jitstress-random
/azp run runtime-coreclr libraries-pgo
/azp run runtime-coreclr libraries-jitstress2-jitstressregs

@azure-pipelines

Copy link
Copy Markdown
No pipelines are associated with this pull request.

@svick

svick commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

/azp list

@azure-pipelines

Copy link
Copy Markdown
CI/CD Pipelines for this repository:

@svick

svick commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr libraries-jitstress-random

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@svick

svick commented Jul 17, 2026

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr libraries-pgo, runtime-coreclr libraries-jitstress2-jitstressregs

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky timeout in CreateFileChangeToken_RootDeletedAndRecreated_TokenFiresWhenFileCreated

2 participants