Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DirectoryInfoWrapper throws DirectoryNotFoundException when directory is deleted during enumeration #44652

Closed
ericstj opened this issue Nov 13, 2020 · 9 comments
Assignees
Labels
area-Extensions-FileSystem blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs test-run-core Test failures in .NET Core test runs
Milestone

Comments

@ericstj
Copy link
Member

ericstj commented Nov 13, 2020

Description

Reported here #44626
Discovered here #41426 (comment)

I thought I had rooted out this issue and solved it by reducing the timer period(and added an extra test to try and capture the issue), but now I suspect there is a codepath here that is creating the timer on a background thread even when the PFW is disposed. So the polling timer is still going then we later delete the directory from under it.

The delete is happening between these two lines, and causing the exception:

if (_directoryInfo.Exists)
{
foreach (FileSystemInfo fileSystemInfo in _directoryInfo.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly))

We could just catch the exception here, since it's already trying to avoid it by checking Exists. I was hoping to find a better fix to identify where to cancel the timer. Perhaps we are canceling the timer but it was already running?

This should fix PollingFileProviderShouldntConsumeINotifyInstances to be less flaky.

@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@ghost
Copy link

ghost commented Nov 13, 2020

Tagging subscribers to this area: @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details
Description:

Description

Reported here #44626
Discovered here #41426 (comment)

I thought I had rooted out this issue and solved it by reducing the timer duration (and added an extra test to try and capture the issue), but now I suspect there is a codepath here that is creating the timer on a background thread even when the PFW is disposed. So the polling timer is still going then we later delete the directory from under it.

The delete is happening between these two lines, and causing the exception:

if (_directoryInfo.Exists)
{
foreach (FileSystemInfo fileSystemInfo in _directoryInfo.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly))

We could just catch the exception here, since it's already trying to avoid it by checking Exists. I was hoping to find a better fix to identify where to cancel the timer. Perhaps we are canceling the timer but it was already running?

Author: ericstj
Assignees: -
Labels:

area-Extensions-FileSystem, untriaged

Milestone: -

@ericstj ericstj added blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs and removed untriaged New issue has not been triaged by the area owner labels Nov 13, 2020
@ericstj ericstj self-assigned this Nov 13, 2020
@ericstj ericstj added this to the 6.0.0 milestone Nov 13, 2020
@ericstj ericstj added the test-run-core Test failures in .NET Core test runs label Nov 13, 2020
@ericstj
Copy link
Member Author

ericstj commented Nov 13, 2020

I believe this is indeed a race condition. The Timer gets disposed by

but nothing will stop a currently running timer callback when this happens.

The problem can occur if a directory is deleted after the exists check and before enumeration starts here:

if (_directoryInfo.Exists)
{
foreach (FileSystemInfo fileSystemInfo in _directoryInfo.EnumerateFileSystemInfos("*", SearchOption.TopDirectoryOnly))

If the deletion happens during enumeration the enumeration will halt, but if it happens at the start an exception is thrown. We can address this by handling the exception, since the existence check makes it clear that this should ignore missing directories.

@ericstj
Copy link
Member Author

ericstj commented Nov 13, 2020

@ericstj ericstj changed the title Disposing PhysicalFileProvider doesn't always Dispose PhysicalFilesWatcher's timer DirectoryInfoWrapper throws DirectoryNotFoundException when directory is deleted during enumeration Nov 13, 2020
@stephentoub
Copy link
Member

but nothing will stop a currently running timer callback when this happens

I've not read the full description to know whether this is relevant, but there are Timer.Dispose{Async} overloads that let you wait until all work associated with the timer has quiesced.

@ericstj
Copy link
Member Author

ericstj commented Nov 14, 2020

I can safely say that it's part of the DirectoryInfoWrapper's contract to not throw on a missing directory regardless so I feel this is the right fix.

@ericstj
Copy link
Member Author

ericstj commented Nov 14, 2020

there are Timer.Dispose{Async} overloads that let you wait until all work associated with the timer has quiesced.

We'd need to add DisposeAsync to PhysicalFilesWatcher to support this, but that's not a bad idea. I'll open an issue on it.

@stephentoub
Copy link
Member

And/or use Timer.Dispose(WaitHandle).

@directhex
Copy link
Member

Closing as resolved by #44671

@ghost ghost locked as resolved and limited conversation to collaborators Apr 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-FileSystem blocking-outerloop Blocking the 'runtime-coreclr outerloop' and 'runtime-libraries-coreclr outerloop' runs test-run-core Test failures in .NET Core test runs
Projects
None yet
Development

No branches or pull requests

5 participants