private unsafe void SignalWorkerThreadsToStop() { Observable.Interval(TimeSpan.FromMilliseconds(25)) .TakeWhile(_ => _runningThreads > 0) .Select(_ => { var result = new SwatcherAsyncResult { Buffer = new byte[0] }; 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); //when using IOCPs, we can send our own custom messages to the GetQueuedCompletionStatus //method by call PostQueuedCompletionStatus. In this case, we want to stop the threads that are //waiting on change events, so we will send a custom completion key "StopIocpThreads". WindowsFacade.PostQueuedCompletionStatus(CompletionPortHandle, 0, StopIocpThreads, overlappedPointer); return(Unit.Default); }) .AsCompletion() .Where(_ => Config.LoggingEnabled) .Subscribe(_ => { Disposables.Dispose(); Logger.Info("Swatcher has stopped"); }); }
private static unsafe int DeserializeMessage( ISwatcherConfig config, SwatcherAsyncResult asyncResult, ref int currentOffset, ISubject <SwatcherCreatedEventArgs> createdSubject, ISubject <SwatcherEventArgs> deletedSubject, ISubject <SwatcherEventArgs> changedSubject, ISubject <RenamedInfo> renamedSubject) { int nextOffset; string name; int @event; fixed(byte *bufferPointer = asyncResult.Buffer) { //buffer pointer was the address where we started. //add current offset to get the address of our the next offset. //4 bytes in an integer ;-). nextOffset = *(int *)(bufferPointer + currentOffset); // the next integer contains the action. @event = *(int *)(bufferPointer + currentOffset + 4); //next int pointer has the address that contains the length of the name //of the item that was created,changed,renamed or deleted. var nameLength = *(int *)(bufferPointer + currentOffset + 8); //finally, retrieve the string via char* using the name length from above. //we divide the length by 2 because a char is 2 bytes. name = new string((char *)(bufferPointer + currentOffset + 12), 0, nameLength / 2); } switch ((FileSystemItemEvent)@event) { case FileSystemItemEvent.Created: OnItemCreatedInternal(config, createdSubject, name); break; case FileSystemItemEvent.Deleted: OnItemDeletedInternal(config, deletedSubject, name); break; case FileSystemItemEvent.Changed: OnItemChangedInternal(config, changedSubject, name); break; case FileSystemItemEvent.RenamedOldName: OnItemRenamedInternal(config, renamedSubject, name, FileSystemItemEvent.RenamedOldName); break; case FileSystemItemEvent.RenamedNewName: OnItemRenamedInternal(config, renamedSubject, name, FileSystemItemEvent.RenamedNewName); break; default: if (config.LoggingEnabled) { Logger.Trace($"[Skipped] An event was skipped because it didn't map to a Swatcher FileSystemEvent. Value={@event}, Name={name ?? "null"}."); } break; } currentOffset += nextOffset; return(nextOffset); }
// 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); } } }