private static unsafe void DoQueuedCompletionWork( ISwatcherConfig config, IWindowsFacade facade, SafeLocalMemHandle completionPortHandle, ISubject <SwatcherCreatedEventArgs> createdSubject, ISubject <SwatcherEventArgs> deletedSubject, ISubject <SwatcherEventArgs> changedSubject, ISubject <RenamedInfo> renamedSubject) { //this is a blocking call... var completionStatus = WaitForCompletionStatus(facade, completionPortHandle); if (completionStatus == null) { return; } var overlapped = Overlapped.Unpack(completionStatus.Value.OverlappedPointer); var asyncResult = (SwatcherAsyncResult)overlapped.AsyncResult; var currentOffset = 0; // ReSharper disable once TooWideLocalVariableScope var nextOffset = 0; try { do { nextOffset = DeserializeMessage( config, asyncResult, ref currentOffset, createdSubject, deletedSubject, changedSubject, renamedSubject); } while (nextOffset != 0); } finally { //*always* free up the overlapped. otherwise, we will have a ~65kb memory leak after each callback. Overlapped.Free(completionStatus.Value.OverlappedPointer); } }
// ReSharper disable once SuggestVarOrType_Elsewhere private static unsafe void WatchFolderForChanges( object wrapper, ISwatcherConfig config, IWindowsFacade windowsFacade, SafeFileHandle directoryHandle) { var result = new SwatcherAsyncResult { Buffer = new byte[DefaultBufferSize] }; var overlapped = new Overlapped { AsyncResult = result }; //the first parameter is null because we're not using IO completion callbacks; they're too slow. //we're taking the byte array from our empty byte array and passing that as user data to the overlapped. var overlappedPointer = overlapped.UnsafePack(null, result.Buffer); var success = false; try { //now we wrap this section in a fixed block to pin it to the original address. //we cannot have it move because the OS will write information about the changes //into this byte array. fixed(byte *bufferPointer = result.Buffer) { var bytesReturned = 0; var bufferHandle = new HandleRef(result, (IntPtr)bufferPointer); var isRecursive = Convert.ToInt32(config.IsRecursive); //because we're using IO completion ports, we pass our overlapped pointer into this unmanaged //call. when a change has been received, the OS will callback via GetQueuedCompletionStatus //passing the overlapped pointer (which has our IAsyncResult/byte array) back to us. success = windowsFacade.ReadDirectoryChangesW( directoryHandle, bufferHandle, DefaultBufferSize, isRecursive, (int)config.NotificationTypes, bytesReturned, overlappedPointer, SafeLocalMemHandle.Empty); //in this usage of ReadDirectoryChangesW, we should *always* get 0 bytes returned. if (bytesReturned != 0) { Debugger.Break(); } } } finally { //if success is false, our directory handle has likely become invalid. attempt to re-establish it. if (!success) { Debugger.Break(); //before doing anything else, cleanup here to prevent memory leaks. Overlapped.Free(overlappedPointer); } } }
internal Swatcher(ISwatcherConfig config, IWindowsFacade windowsFacade) { Config = config; WindowsFacade = windowsFacade; var createdEventStream = CreateCreatedEventStream(); var finishedCreatedEventStream = CreateFinishedCreatedEventStream(createdEventStream); finishedCreatedEventStream .Delay(TimeSpan.FromSeconds(2.5)) .Subscribe(x => CreatedEventsInProgress.Remove(x.FullPath)); var createdEventWindows = CreateCreatedEventWindowStream(createdEventStream); var finishedCreatedEventWindows = CreateFinishedCreatedEventWindows(finishedCreatedEventStream); var changedEventStream = CreateChangedEventStream(); var changedEventWindows = CreatedChangedEventWindows(changedEventStream); Renamed = CreatePublicRenamedStream(Config); Deleted = CreatePublicDeletedStream(); Created = CreatePublicCreatedStream(finishedCreatedEventStream); Changed = CreatePublicChangedStream(changedEventStream, changedEventWindows, createdEventWindows, finishedCreatedEventWindows); ChangedEventPatternSource = Changed.Select(x => new EventPattern <SwatcherEventArgs>(this, x)).ToEventPattern(); ChangedEventPatternSource.OnNext += OnItemChanged; DeletedEventPatternSource = Deleted.Select(x => new EventPattern <SwatcherEventArgs>(this, x)).ToEventPattern(); DeletedEventPatternSource.OnNext += OnItemDeleted; CreatedEventPatternSource = Created.Select(x => new EventPattern <SwatcherCreatedEventArgs>(this, x)).ToEventPattern(); CreatedEventPatternSource.OnNext += OnItemCreated; RenamedEventPatternSource = Renamed.Select(x => new EventPattern <SwatcherRenamedEventArgs>(this, x)).ToEventPattern(); RenamedEventPatternSource.OnNext += OnItemRenamed; }
private static unsafe QueuedCompletionStatus?WaitForCompletionStatus( IWindowsFacade facade, SafeLocalMemHandle completionPortHandle) { uint bytesRead; uint completionKey; NativeOverlapped *overlappedPointer; //when change events have occurred, the OS will callback on this blocking method. //the key is that we provide the completion port handle, which has been bound to the //directory handle being watched (see InitializeCompletionPort() method). we get our change data //by passing a pointer by reference to our nativeoverlapped -> IAsyncResult -> buffer. var result = facade.GetQueuedCompletionStatus( completionPortHandle, out bytesRead, out completionKey, &overlappedPointer, InfiniteTimeout); //I don't think that this has ever returned false during testing. //if (!result) // Debugger.Break(); if (completionKey == StopIocpThreads) { Logger.Trace($"Stopping {Thread.CurrentThread.Name}"); Thread.CurrentThread.Abort(); return(null); } if (bytesRead == 0) { return(null); } return(new QueuedCompletionStatus() { BytesRead = bytesRead, CompletionKey = completionKey, OverlappedPointer = overlappedPointer }); }