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

Commit 28132be

Browse files
committed
Merge pull request #2674 from stephentoub/fix_fsw_threadpoolbinding
Fix FSW freeing of NativeOverlapped on wrong ThreadPoolBoundHandle
2 parents 82f8331 + 0f67204 commit 28132be

File tree

1 file changed

+21
-12
lines changed

1 file changed

+21
-12
lines changed

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

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,11 @@ private void FinalizeDispose()
108108
// Thread gate holder and constats
109109
private bool _stopListening = false;
110110

111-
private bool IsHandleInvalid
111+
private bool IsHandleInvalid { get { return IsHandleInstanceInvalid(_directoryHandle); } }
112+
113+
private bool IsHandleInstanceInvalid(SafeFileHandle handle)
112114
{
113-
get
114-
{
115-
return (_directoryHandle == null || _directoryHandle.IsInvalid || _directoryHandle.IsClosed);
116-
}
115+
return handle == null || handle.IsInvalid || handle.IsClosed;
117116
}
118117

119118
/// <devdoc>
@@ -132,12 +131,21 @@ private unsafe void Monitor(byte[] buffer)
132131
buffer = AllocateBuffer();
133132
}
134133

134+
// Use a local copy of _threadPoolBinding for the remainder of the method.
135+
// Otherwise, the value of the field could get replaced by a different
136+
// instance if events are stopped and then restarted, and we could end up
137+
// trying to use the new binding with an overlapped created on the old binding.
138+
// Similarly for the directory handle.
139+
ThreadPoolBoundHandle threadPoolBinding = _threadPoolBinding;
140+
SafeFileHandle directoryHandle = _directoryHandle;
141+
135142
// Pass "session" counter to callback:
136143
FSWAsyncResult asyncResult = new FSWAsyncResult();
137144
asyncResult.session = _currentSession;
138145
asyncResult.buffer = buffer;
146+
asyncResult.threadPoolBinding = threadPoolBinding;
139147

140-
NativeOverlapped* overlappedPointer = _threadPoolBinding.AllocateNativeOverlapped(new IOCompletionCallback(this.CompletionStatusChanged), asyncResult, buffer);
148+
NativeOverlapped* overlappedPointer = threadPoolBinding.AllocateNativeOverlapped(new IOCompletionCallback(this.CompletionStatusChanged), asyncResult, buffer);
141149

142150
// Can now call OS:
143151
int size;
@@ -149,12 +157,12 @@ private unsafe void Monitor(byte[] buffer)
149157
// and when we get here from CompletionStatusChanged.
150158
// We might need to take a lock to prevent race absolutely, instead just catch
151159
// ObjectDisposedException from SafeHandle in case it is disposed
152-
if (!IsHandleInvalid)
160+
if (!IsHandleInstanceInvalid(directoryHandle))
153161
{
154162
// An interrupt is possible here
155163
fixed (byte* buffPtr = buffer)
156164
{
157-
ok = UnsafeNativeMethods.ReadDirectoryChangesW(_directoryHandle,
165+
ok = UnsafeNativeMethods.ReadDirectoryChangesW(directoryHandle,
158166
buffPtr,
159167
_internalBufferSize,
160168
_includeSubdirectories ? 1 : 0,
@@ -170,17 +178,17 @@ private unsafe void Monitor(byte[] buffer)
170178
}
171179
catch (ArgumentNullException)
172180
{ //Ignore
173-
Debug.Assert(IsHandleInvalid, "ArgumentNullException from something other than SafeHandle?");
181+
Debug.Assert(IsHandleInstanceInvalid(directoryHandle), "ArgumentNullException from something other than SafeHandle?");
174182
}
175183
finally
176184
{
177185
if (!ok)
178186
{
179-
_threadPoolBinding.FreeNativeOverlapped(overlappedPointer);
187+
threadPoolBinding.FreeNativeOverlapped(overlappedPointer);
180188

181189
// If the handle was for some reason changed or closed during this call, then don't throw an
182190
// exception. Else, it's a valid error.
183-
if (!IsHandleInvalid)
191+
if (!IsHandleInstanceInvalid(directoryHandle))
184192
{
185193
OnError(new ErrorEventArgs(new Win32Exception()));
186194
}
@@ -354,7 +362,7 @@ rename event with the Name field null and then fire the next action.
354362
}
355363
finally
356364
{
357-
_threadPoolBinding.FreeNativeOverlapped(overlappedPointer);
365+
asyncResult.threadPoolBinding.FreeNativeOverlapped(overlappedPointer);
358366
if (!_stopListening)
359367
{
360368
Monitor(asyncResult.buffer);
@@ -368,6 +376,7 @@ private sealed class FSWAsyncResult
368376
{
369377
internal int session;
370378
internal byte[] buffer;
379+
internal ThreadPoolBoundHandle threadPoolBinding;
371380
}
372381

373382
/// <devdoc>

0 commit comments

Comments
 (0)