Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 6568da5

Browse files
committed
Fix regression in FileSystemWatcher
As part of the bug fix in f2720ab , a regression was introduced, one that we didn't catch until recently due to not having a test that stressed stopping and quickly restarting an FSW doing monitoring. At the time of that fix, the active asynchronous monitoring operation didn't have its own copy of the directory handle; instead, it just used the single field on the FSW instance. As such, in a race condition between the asynchronous monitoring operation trying to use that instance and the main thread stopping the FSW and nulling out the field, the asynchronous monitoring operation could experience a NullReferenceException. The fix was to stop nulling out the field and just rely on closing the handle being enough to signal that monitoring should stop. However, this causes a problem in a particular race condition. While a SafeHandle is in use by a P/Invoke operation, its reference count is temporarily incremented so as to prevent it from being closed while in use. This means that if the directory handle is disposed of while it's being used by ReadDirectoryChangesW, the handle's IsClosed won't return true until after ReadDirectoryChangesW returns. When the FSW is stopped, Dispose is called on the handle, and then when it's restarted, the handle's IsClosed is checked to see if there's currently an active monitoring operation, and if there is, the FSW assumes it doesn't need to restart anything. But if the stop and the start both occur during the call to ReadDirectoryChangesW, then the start won't actually start anything. My reliability/performance fixes in b6c478c eliminated the reliance on the shared directory handle field: the asynchronous monitoring operation has its own copy. Thus, there's no longer a problem in nulling out the directory handle field. The fix for the regression is simply to do so. (Long explanation for a fix that's simply to null out a field :-)
1 parent f6e99e3 commit 6568da5

File tree

2 files changed

+6
-2
lines changed

2 files changed

+6
-2
lines changed

src/System.IO.FileSystem.Watcher/src/System/IO/FileSystemWatcher.Win32.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,13 @@ private void StopRaisingEvents()
7575
// This operation doesn't need to be atomic because the API will deal with a closed
7676
// handle appropriately. If we get here while asynchronously waiting on a change notification,
7777
// closing the directory handle should cause ReadDirectoryChangesCallback be be called,
78-
// cleaning up the operation.
78+
// cleaning up the operation. Note that it's critical to also null out the handle. If the
79+
// handle is currently in use in a P/Invoke, it will have its reference count temporarily
80+
// increased, such that the disposal operation won't take effect and close the handle
81+
// until that P/Invoke returns; if during that time the FSW is restarted, the IsHandleInvalid
82+
// check will see a valid handle, unless we also null it out.
7983
_directoryHandle.Dispose();
84+
_directoryHandle = null;
8085

8186
// Set enabled to false
8287
_enabled = false;

src/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Changed.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ public static void FileSystemWatcher_Changed_FileDataChange()
146146
}
147147
}
148148

149-
[ActiveIssue(2740)]
150149
[Theory]
151150
[InlineData(true)]
152151
[InlineData(false)]

0 commit comments

Comments
 (0)