5

I'm using C# sockets(which use IOCP for callbacks). I want a method to determine weather my processing logic is falling behind. Is there an API call that could give me the size of completed operations that haven't been processed by callbacks?

I have considered using something like a heartbeat operation that I would post to the queue and determine if i'm behind by the elapsed time to its callback, but i would prefer a more direct route if possible(Plus I don't have easy access to the IOCP handle that .Net internals controls).

Dozer789
  • 1,980
  • 2
  • 23
  • 43
Lawrence Ward
  • 549
  • 1
  • 5
  • 17

1 Answers1

4

Not via a documented API but you could try this...

/*
GetIocpQueueCount

Description:
Returns the number of queued IOCP work and I/O completion items.

Remarks:
Microsoft declined to implement the NtQueryIoCompletion routine
for user mode. This function gets past that omission by calling
the NTDLL.DLL function dynamically.

Returns:
Number of items in the queue at the instant this function was
called, or -1 if an error occurred. Errors can be retrieved
by calling GetLastError.
*/
long GetIocpQueueCount()
{
   long lQueueDepth = -1;

   typedef DWORD (WINAPI *LPFNNTQUERYIOCOMPLETION)(HANDLE, int, PVOID, ULONG, PULONG);

   static LPFNNTQUERYIOCOMPLETION pfnNtQueryIoCompletion = NULL;

   if (MFTASKQUEUENOTCREATED != m_dwStatus)
   {
      DWORD rc = NO_ERROR;

      /* need to load dynamically */

      if (NULL == pfnNtQueryIoCompletion)
      {
         /* Now dynamically obtain the undocumented NtQueryIoCompletion
          * entry point from NTDLL.DLL
          */

         HMODULE hmodDll = ::GetModuleHandleW(L"ntdll.dll");

         // NTDLL is always loaded, just get its handle

         if (NULL != hmodDll)
         {
            pfnNtQueryIoCompletion = (LPFNNTQUERYIOCOMPLETION)::GetProcAddress(
               hmodDll,
               "NtQueryIoCompletion"); // NB: ANSI
         } 
      }

      if (NULL != pfnNtQueryIoCompletion)
      {
         rc = (pfnNtQueryIoCompletion)(
            m_hIOCP,
            0,
            (PVOID)&lQueueDepth,
            sizeof(lQueueDepth),
            NULL);
      }
      else
      {
         rc = ERROR_NOT_FOUND;
      }
      ::SetLastError(rc);
   }
   return lQueueDepth;
}
Len Holgate
  • 21,282
  • 4
  • 45
  • 92
  • That's pretty neat, but .NET doesn't expose its internally created `m_hIOCP`. – dtb Jul 23 '13 at 23:32
  • this is awesome! @dtb no problem, i can extract it with reflection. but regardless, i think using a heartbeat will give me a more realistic view of how far behind i am. but implementing the heartbeat will be even more troublesome than this simple method since i have no way to implement a custom handler on the .net IOCP threadpool. i would have to use a socket connection to my server to implement the heartbeat. – Lawrence Ward Jul 24 '13 at 07:05
  • If you can extract the handle with reflection, please post your code somewhere. I've been trying that for ages. – dtb Jul 24 '13 at 08:15
  • ok, it seem i cant, the juicy work for the IOCP is hidden behind `MethodImpl(MethodImplOptions.InternalCall)`.... dang, i was optimistic since i saw some code that implemented the threadpool, but it looks like its between a `#if SOCKETTHREADPOOL` which is probably not defined. – Lawrence Ward Jul 24 '13 at 10:02