How to determine if a non-IFS LSP is installed

| 0 Comments

Enabling FILE_SKIP_COMPLETION_PORT_ON_SUCCESS on a handle associated with an I/O completion port can improve performance and reduce context switching by allowing the thread that calls the API that can complete asynchronously to handle the completion "inline" if the call can complete synchronously. This is especially useful for TCP reads when there's already data in the network stack's buffers, or writes when there is space in the buffers. Whilst there are design issues that must be taken into consideration before simply enabling this flag (beware recursion!) there's a little known issue where code outside of your control can prevent the IOCP from operating correctly when this flag is enabled.

If non-IFS Winsock Base Service Providers (BSPs) or Layered Service Providers (LSPs) are installed then you may not receive completions at all for handles with the flag set.

This Microsoft Knowledge Base article has been around for quite a few years and it's important and possibly becoming more so as more and more poorly written LSPs get installed by adware and other rubbish.

It's all very well knowing that you can't use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS when you have a non-IFS LSP installed but how can you tell at runtime that this is the case?

We've had this code in The Server Framework since 2011 or so: it iterates the Winsock catalog and lets you know if any non-IFS LSPs are installed. You can then use the results of this to determine if it's safe to enable FILE_SKIP_COMPLETION_PORT_ON_SUCCESS or not.

static const int s_protocols[] = { IPPROTO_TCP, IPPROTO_UDP, 0 };

bool CanEnableSkipCompletionPortOnSuccess()
{
   // At some point we MAY want to check for UDP and TCP transports separately,
   // if we do that then we need to change this.

   TExpandableBuffer<BYTE> buffer;

   LPWSAPROTOCOL_INFOW pProtocolInfo = nullptr;

   DWORD bufferLength = 0;

   int error = 0;

   int numEntries = ::WSCEnumProtocols(
      const_cast<int *>(&s_protocols[0]),
      pProtocolInfo,
      &bufferLength,
      &error);

   if (SOCKET_ERROR != numEntries)
   {
      throw CException(
         _T("CanEnableSkipCompletionPortOnSuccess()"),
         _T("Expected first call to fail and return buffer size!"));
   }

   int attempts = 0;

   bool done = false;

   while (!done)
   {
      if (error != WSAENOBUFS)
      {
         throw CWin32Exception(
            _T("CanEnableSkipCompletionPortOnSuccess()"),
            error);
      }

      if (attempts++ > 3)
      {
         // so the amount of memory required is always changing??

         throw CException(
            _T("CanEnableSkipCompletionPortOnSuccess()"),
            _T("Cannot allocate appropriate buffer: ") +
            ToString(bufferLength));
      }

      buffer.Resize(bufferLength);

      pProtocolInfo = reinterpret_cast<LPWSAPROTOCOL_INFOW>(buffer.GetBuffer());

      numEntries = ::WSCEnumProtocols(
         const_cast<int *>(&s_protocols[0]),
         pProtocolInfo,
         &bufferLength,
         &error);

      done = (SOCKET_ERROR != numEntries);
   }

   bool ok = true;

   for (int i = 0; ok && i < numEntries; ++i)
   {
      ok = ((pProtocolInfo[i].dwServiceFlags1 & XP1_IFS_HANDLES) == XP1_IFS_HANDLES);
   }

   return ok;
}

Leave a comment

About this Entry

C++ Tools - JetBrains ReSharper C++ - purchased... was the previous entry in this blog.

VMWare bridged networking intermittently failing is the next entry in this blog.

I usually write about C++ development on Windows platforms, but I often ramble on about other less technical stuff...

Find recent content on the main index or look in the archives to find all content.

I have other blogs...

Subscribe to feed The Server Framework - high performance server development
Subscribe to feed Lock Explorer - deadlock detection and multi-threaded performance tools
Subscribe to feed l'Hexapod - embedded electronics and robotics
Subscribe to feed MegèveSki - skiing