/// <summary> /// Iocp completion thread using GetQueuedCompletionStatusEx(). /// You would expect to get benefit from this over GetQueuedCompletionStatus() but in tests /// we've not seen that, at least not yet, but you can select it at ctor time hoping we /// can find a way to unlock its perf in future. /// </summary> /// <param name="o"></param> private void IocpThreadProcEx(object o) { try { Console.WriteLine("IoFlowRuntime: using experimental GetQueuedCompletionStatusEx() thread."); // // IOCP thread init: start multiple concurrent READ IRPS to driver. // Note all I/O must originate on this thread for Win32 CancelIo to work. // for (int i = 0; i < Parameters.COUNT_IO_READ_MPL; i++) { IRP irp = AllocateIrp(); irp.StartMiniFilterGetMessage(); } ThreadPriority oldThreadPriority = Thread.CurrentThread.Priority; Thread.CurrentThread.Priority = ThreadPriority.Highest; // // Prep for GetQueuedCompletionStatusEx(). // IntPtr lpOverlapped; uint lpNumberOfBytes; uint lpCompletionKey; OVERLAPPED_ENTRY[] ArrayOverlappedEntry = new OVERLAPPED_ENTRY[IocpSizeofOverlappedArray]; GCHandle gcArrayOverlappedEntry = GCHandle.Alloc(ArrayOverlappedEntry, GCHandleType.Pinned); IntPtr addrArrayOverlappedEntry = gcArrayOverlappedEntry.AddrOfPinnedObject(); uint NumEntriesRemoved = 1; FLT_PREOP_CALLBACK_STATUS PreOpResult = FLT_PREOP_CALLBACK_STATUS.FLT_PREOP_SUCCESS_WITH_CALLBACK; FLT_POSTOP_CALLBACK_STATUS PostOpResult = FLT_POSTOP_CALLBACK_STATUS.FLT_POSTOP_FINISHED_PROCESSING; // // IOCP main loop: think -- {completePrior; flow.Callback(IRP); startNext}. // while (ShuttingDown == false) // IOCP main loop. { // // Block until IoCompletionPort indicates a) I/O completion or b) some signal. // if (GetQueuedCompletionStatusEx(hCompletionPort, addrArrayOverlappedEntry, IocpSizeofOverlappedArray, out NumEntriesRemoved, Parameters.IocpTimeoutInfinite, // Parameters.IocpTimeoutMSecs, //WIN32_INFINITE, false) == false) { int HResult = Marshal.GetHRForLastWin32Error(); int LastWin32Error = Marshal.GetLastWin32Error(); // // Expect to get here when Close() has been called. // const uint THE_HANDLE_IS_INVALID = 0x80070006; // Not an error. const uint ERROR_ABANDONED_WAIT_0 = 0x800702DF; // Not an error. if ((uint)HResult == THE_HANDLE_IS_INVALID || (uint)HResult == ERROR_ABANDONED_WAIT_0) { break; } // // In absence of I/O load, iocp timeout opt triggers polled queued I/O support. // if (LastWin32Error == WIN32_WAIT_TIMEOUT) { continue; } // // Handle I/O failures. Non-fatal iff timeout or if media disconnected. // if (LastWin32Error == WIN32_ERROR_GEN_FAILURE) { Console.WriteLine("warning: ignored iocp tx status ERROR_GEN_FAILURE"); continue; } // // Unexpected error. // Console.WriteLine("LastWin32Error {0} HResult {1:X8}", LastWin32Error, HResult); Marshal.ThrowExceptionForHR((int)HResult); } // // One iteration per entry in array returned from I/O completion port. // for (int i = 0; i < (int)NumEntriesRemoved; i++) { lpOverlapped = ArrayOverlappedEntry[i].addrOverlapped; lpCompletionKey = (uint)ArrayOverlappedEntry[i].lpCompletionKey.ToInt32(); // // Get from native addr of completing OVERLAPPED struct to the associated managed code IRP. // IRP irp = FindIrp[lpOverlapped]; irp.CompleteMiniFilterGetMessage(); // // Find the IoFlow for this IRP and call the user's callback function. // LockIoFlow.EnterReadLock(); irp.IoFlow = DictIoFlow[irp.IoFlowHeader.FlowId]; LockIoFlow.ExitReadLock(); // // Indicate any late errors for earlier IO indicated on this flow. // These can happen if an error happens in the driver *after* the // filterMgr messaging API has indicated that the reply message has // been successfully delivered to the driver. In priciple the owning // app should also know due to IO failure. // if ((irp.IoFlowHeader.Flags & (uint)HeaderFlags.HeaderFlagLateIoError) != 0) { string msg = "One or more errors reported for earlier IO on flowId "; msg = string.Format("{0} {1} file {2}", msg, irp.IoFlow.FlowId, irp.IoFlow.FileName); throw new ExceptionIoFlow(msg); } // // Throw iff the kmode IRP's buffer is too large for our k2u buffers. // In this case we see incomplete data so any ops on the data are invalid. // if (irp.DataLength > irp.MaxDataLength) { string msg = string.Format("Err irp.DataLength({0}) > k2u buffer size({1})", irp.DataLength, irp.MaxDataLength); throw new ExceptionIoFlow(msg); } // // Upcall to user-supplied callback routine. // Note the minifilter only sends IRPs for flows that have registered an appropriate callback routine. // bool IsPreOp = irp.IsPreOp, IsPostOp = !IsPreOp; switch (irp.MajorFunction) { case MajorFunction.IRP_MJ_CREATE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCreate(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCreate(irp); } break; } case MajorFunction.IRP_MJ_READ: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreRead(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostRead(irp); } break; } case MajorFunction.IRP_MJ_WRITE: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreWrite(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostWrite(irp); } break; } case MajorFunction.IRP_MJ_CLEANUP: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCleanup(irp); } else { PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCleanup(irp); } break; } case MajorFunction.IRP_MJ_LOCK_CONTROL: { if (IsPreOp) { PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreLockControl(irp); } else { throw new NotImplementedException(String.Format("irp.IoFlow.FlowId {0} PostLockControl callback not supported.", irp.IoFlow.FlowId)); } break; } default: throw new ApplicationException("should never get here."); } irp.SetStateCallbackReturned(); irp.IoFlowHeader.FilterReplyHeader.Status = 0; irp.IoFlowHeader.Resultcode = (irp.IsPreOp ? (uint)PreOpResult : (uint)PostOpResult); #if UseReplyAndGetNext // // App is done with this IRP. // Send reply to minifilter driver and restart next upcall on same IRP. // irp.MiniFilterReplyAndGetNext(hDataAsyncPort, hControlPort); #else // UseReplyAndGetNext // // Send reply to our minifilter driver. // irp.MiniFilterReplyMessage(hDataAsyncPort, hControlPort); // // The IRP is no longer in use by app - we can restart next upcall request on same IRP. // irp.StartMiniFilterGetMessage(); #endif // UseReplyAndGetNext } //for (int i = 0; i < (int)NumEntriesRemoved; i++) } // while (ShuttingDown == false) // IOCP main loop. // // Shut down. // CancelIo(hDataAsyncPort); } catch (ThreadInterruptedException CaughtException) { if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) { LastThreadTerminated.Set(); } if (ShuttingDown) { return; } throw new ThreadInterruptedException(null, CaughtException); } if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) { LastThreadTerminated.Set(); } }
/// <summary> /// Iocp completion thread using GetQueuedCompletionStatusEx(). /// You would expect to get benefit from this over GetQueuedCompletionStatus() but in tests /// we've not seen that, at least not yet, but you can select it at ctor time hoping we /// can find a way to unlock its perf in future. /// </summary> /// <param name="o"></param> private void IocpThreadProcEx(object o) { try { Console.WriteLine("IoFlowRuntime: using experimental GetQueuedCompletionStatusEx() thread."); // // IOCP thread init: start multiple concurrent READ IRPS to driver. // Note all I/O must originate on this thread for Win32 CancelIo to work. // for (int i = 0; i < Parameters.COUNT_IO_READ_MPL; i++) { IRP irp = AllocateIrp(); irp.StartMiniFilterGetMessage(); } ThreadPriority oldThreadPriority = Thread.CurrentThread.Priority; Thread.CurrentThread.Priority = ThreadPriority.Highest; // // Prep for GetQueuedCompletionStatusEx(). // IntPtr lpOverlapped; uint lpNumberOfBytes; uint lpCompletionKey; OVERLAPPED_ENTRY[] ArrayOverlappedEntry = new OVERLAPPED_ENTRY[IocpSizeofOverlappedArray]; GCHandle gcArrayOverlappedEntry = GCHandle.Alloc(ArrayOverlappedEntry, GCHandleType.Pinned); IntPtr addrArrayOverlappedEntry = gcArrayOverlappedEntry.AddrOfPinnedObject(); uint NumEntriesRemoved = 1; FLT_PREOP_CALLBACK_STATUS PreOpResult = FLT_PREOP_CALLBACK_STATUS.FLT_PREOP_SUCCESS_WITH_CALLBACK; FLT_POSTOP_CALLBACK_STATUS PostOpResult = FLT_POSTOP_CALLBACK_STATUS.FLT_POSTOP_FINISHED_PROCESSING; // // IOCP main loop: think -- {completePrior; flow.Callback(IRP); startNext}. // while (ShuttingDown == false) // IOCP main loop. { // // Block until IoCompletionPort indicates a) I/O completion or b) some signal. // if (GetQueuedCompletionStatusEx(hCompletionPort, addrArrayOverlappedEntry, IocpSizeofOverlappedArray, out NumEntriesRemoved, Parameters.IocpTimeoutInfinite, // Parameters.IocpTimeoutMSecs, //WIN32_INFINITE, false) == false) { int HResult = Marshal.GetHRForLastWin32Error(); int LastWin32Error = Marshal.GetLastWin32Error(); // // Expect to get here when Close() has been called. // const uint THE_HANDLE_IS_INVALID = 0x80070006; // Not an error. const uint ERROR_ABANDONED_WAIT_0 = 0x800702DF; // Not an error. if ((uint)HResult == THE_HANDLE_IS_INVALID || (uint)HResult == ERROR_ABANDONED_WAIT_0) break; // // In absence of I/O load, iocp timeout opt triggers polled queued I/O support. // if (LastWin32Error == WIN32_WAIT_TIMEOUT) continue; // // Handle I/O failures. Non-fatal iff timeout or if media disconnected. // if (LastWin32Error == WIN32_ERROR_GEN_FAILURE) { Console.WriteLine("warning: ignored iocp tx status ERROR_GEN_FAILURE"); continue; } // // Unexpected error. // Console.WriteLine("LastWin32Error {0} HResult {1:X8}", LastWin32Error, HResult); Marshal.ThrowExceptionForHR((int)HResult); } // // One iteration per entry in array returned from I/O completion port. // for (int i = 0; i < (int)NumEntriesRemoved; i++) { lpOverlapped = ArrayOverlappedEntry[i].addrOverlapped; lpCompletionKey = (uint)ArrayOverlappedEntry[i].lpCompletionKey.ToInt32(); // // Get from native addr of completing OVERLAPPED struct to the associated managed code IRP. // IRP irp = FindIrp[lpOverlapped]; irp.CompleteMiniFilterGetMessage(); // // Find the IoFlow for this IRP and call the user's callback function. // LockIoFlow.EnterReadLock(); irp.IoFlow = DictIoFlow[irp.IoFlowHeader.FlowId]; LockIoFlow.ExitReadLock(); // // Indicate any late errors for earlier IO indicated on this flow. // These can happen if an error happens in the driver *after* the // filterMgr messaging API has indicated that the reply message has // been successfully delivered to the driver. In priciple the owning // app should also know due to IO failure. // if ((irp.IoFlowHeader.Flags & (uint)HeaderFlags.HeaderFlagLateIoError) != 0) { string msg = "One or more errors reported for earlier IO on flowId "; msg = string.Format("{0} {1} file {2}", msg, irp.IoFlow.FlowId, irp.IoFlow.FileName); throw new ExceptionIoFlow(msg); } // // Throw iff the kmode IRP's buffer is too large for our k2u buffers. // In this case we see incomplete data so any ops on the data are invalid. // if (irp.DataLength > irp.MaxDataLength) { string msg = string.Format("Err irp.DataLength({0}) > k2u buffer size({1})", irp.DataLength, irp.MaxDataLength); throw new ExceptionIoFlow(msg); } // // Upcall to user-supplied callback routine. // Note the minifilter only sends IRPs for flows that have registered an appropriate callback routine. // bool IsPreOp = irp.IsPreOp, IsPostOp = !IsPreOp; switch (irp.MajorFunction) { case MajorFunction.IRP_MJ_CREATE: { if (IsPreOp) PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCreate(irp); else PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCreate(irp); break; } case MajorFunction.IRP_MJ_READ: { if (IsPreOp) PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreRead(irp); else PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostRead(irp); break; } case MajorFunction.IRP_MJ_WRITE: { if (IsPreOp) PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreWrite(irp); else PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostWrite(irp); break; } case MajorFunction.IRP_MJ_CLEANUP: { if (IsPreOp) PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreCleanup(irp); else PostOpResult = (FLT_POSTOP_CALLBACK_STATUS)irp.IoFlow.PostCleanup(irp); break; } case MajorFunction.IRP_MJ_LOCK_CONTROL: { if (IsPreOp) PreOpResult = (FLT_PREOP_CALLBACK_STATUS)irp.IoFlow.PreLockControl(irp); else throw new NotImplementedException(String.Format("irp.IoFlow.FlowId {0} PostLockControl callback not supported.", irp.IoFlow.FlowId)); break; } default: throw new ApplicationException("should never get here."); } irp.SetStateCallbackReturned(); irp.IoFlowHeader.FilterReplyHeader.Status = 0; irp.IoFlowHeader.Resultcode = (irp.IsPreOp ? (uint)PreOpResult : (uint)PostOpResult); #if UseReplyAndGetNext // // App is done with this IRP. // Send reply to minifilter driver and restart next upcall on same IRP. // irp.MiniFilterReplyAndGetNext(hDataAsyncPort, hControlPort); #else // UseReplyAndGetNext // // Send reply to our minifilter driver. // irp.MiniFilterReplyMessage(hDataAsyncPort, hControlPort); // // The IRP is no longer in use by app - we can restart next upcall request on same IRP. // irp.StartMiniFilterGetMessage(); #endif // UseReplyAndGetNext } //for (int i = 0; i < (int)NumEntriesRemoved; i++) } // while (ShuttingDown == false) // IOCP main loop. // // Shut down. // CancelIo(hDataAsyncPort); } catch (ThreadInterruptedException CaughtException) { if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) LastThreadTerminated.Set(); if (ShuttingDown) return; throw new ThreadInterruptedException(null, CaughtException); } if (Interlocked.Decrement(ref CountIocpThreadsRunning) == 0) LastThreadTerminated.Set(); }