Keeping socket events coming and receiving all data

From: MWFox (MWFox_at_discussions.microsoft.com)
Date: 08/18/04


Date: Tue, 17 Aug 2004 21:37:01 -0700

We're using CSocket, CSocketFile, and CArchive in our project. In an effort
to keep this posting as streamlined as possible, I ask you to read first my
original posting which describes the problem I was seeing and the environment
in more detail. Please refer to "WM_SOCKET_NOTIFY just stops", dated
8/4/2004.

The reply I received from George N to the original posting boosted my
confidence in my approach to a workaround. I understood George's answer to
mean this: as long as I reenable socket event notification before our
thread's message loop runs again, there likely won't be any loss of events.

An additional concern which I am expressing here for the first time is that
it seems possible for there to be a race condition between our thread's
process of reenabling socket event notification and the background socket
thread's activity of handling port I/O.

In an effort to eliminate these cases I wrote the following code.

Have I covered both cases: 1) keeping WM_SOCKET_NOTIFY events alive and 2)
preventing the accidental loss of data?

Have I shown you enough code for you to answer?

Advice is greatly appreciated.
-Mary

---------------------------------------------
bool CsConnection::EnableSocketIOEvents(void)
{
  bool bIsSuccess = true;

  if (!IsEmpty())
  {
    if (!m_pSocket->AsyncSelect())
    {
      bIsSuccess = false;
      int nErrorCode = GetLastError();
      blah blah blah
    }
  }
  return bIsSuccess;
}

---------------------------------------------
int CsConnection::PollForSocketData(void)
{
  int nRetVal = -2; // default value; means port is not initialized

  if (m_pSocket && m_pSocketFile && m_pArcOut)
  {
    if (!m_bIsCanceled)
    {
      // Note: this is based on the logic in MFC's sockcore.cpp
      // when processing a WM_SOCKET_NOTIFY socket window event
      // to read from the port
      int nErrorCode=0;
    
      fd_set fds;
      int nReady;
      timeval timeout;

      timeout.tv_sec = 0;
      timeout.tv_usec = 0;

      FD_ZERO(&fds);
      FD_SET(m_pSocket->m_hSocket, &fds);

      nReady = select(0, &fds, NULL, NULL, &timeout);

      if (nReady == SOCKET_ERROR)
         nErrorCode = WSAGetLastError();

      // nReady == 1 means the one specified socket is ready for reading
      if ((nReady == 1) || (nErrorCode != 0))
      {
        TRACE_IT(_T("Poll - Calling OnReceive() to serialize.\n"));
        m_pSocket->OnReceive(nErrorCode);
      }

      nRetVal = nReady;
    }
  }
  return nRetVal;
}

---------------------------------------------
UINT CsConnectionThread::MessageLoop(LPVOID lpParam)
{
  CsConnection *pConnection = (CsConnection *)lpParam;

  ASSERT(pConnection);

  CsConnectionErrorHandler *pRegisteredErrorHandler=NULL;
  BOOL bResetSuccess = FALSE;
  LRESULT lResult = CONN_SUCCESS;

  // allocate array to hold "waitable objects" to wakeup up on
  INT nCntEvents = 8;
  HANDLE aEvents[8];

  // fill the array with handles to the "waitable objects"
  aEvents[0] = pConnection->GetReqOutWakeup();
    . . .
  aEvents[7] = pConnection->GetHousekeepingTimer(); // waitable timer #3

  while (TRUE)
  {
      DWORD result ;
      MSG msg ;

      if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
        // WORKAROUND #1 for WM_SOCKET_NOTIFY stoppage:
        // In case socket i/o thread beat EnableSocketIOEvents() to its
completion
        // in this possible race condition, poll the socket port for
        // status and process data at the port if necessary.

        if (pConnection->PollForSocketData() == 0)
        {
          // If nothing at port, reenable WM_SOCKET_* events for good measure.
          // It is possible that earlier we inadvertantly disabled
WM_SOCKET_*
          // events by reading data at the port that was intended to be
          // associated with a WM_SOCKET_NOTIFY we haven't processed yet.

          pConnection->EnableSocketIOEvents();
        }
      }
      else
      {
        if (msg.message == WM_QUIT)
        {
          return msg.wParam;
        }
        // Otherwise, dispatch the WM_SOCKET_* message
        // to the socket window inherently associated with this thread.
        DispatchMessage(&msg);
      }

      // This is the regular Windows message processing loop.
      while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
        if (msg.message == WM_QUIT)
        {
          return msg.wParam;
        }
        // Otherwise, dispatch the WM_SOCKET_* message
        DispatchMessage(&msg);
      }

      // Wait for any message sent or posted to this thread's message queue
      // or for one of the passed handles be set to signaled.
      
      result = MsgWaitForMultipleObjectsEx(nCntEvents, aEvents, INFINITE,
                                            QS_ALLINPUT, MWMO_INPUTAVAILABLE);

      // WORKAROUND #2 for WM_SOCKET_NOTIFY stoppage:
      // Request socket thread to notify this thread of socket i/o
      // by sending WM_SOCKET_* windows messages.
      // Performance is best if done after call to MWMOEx().

      pConnection->EnableSocketIOEvents();

      if (result == (WAIT_OBJECT_0 + nCntEvents))
      {
          continue;
      }
      else
      {
        // event became signaled.
        if (result - WAIT_OBJECT_0 == 0)
        {
          . . . handle event . . .
        }
        else if (result - WAIT_OBJECT_0 == 1)
        {
          . . . handle event . . .
        }
          . . . etc. . . .
      }
  } // end forever loop
} //---End of CsConnectionThread::MessageLoop

P.S. Replies to these postings were also helpful:
"CAsyncSocket::Send does not appear to send a WM_SOCKET_NOTIFY"
and "BUG in Winsock on P4 HT CPU"



Relevant Pages

  • Re: Accessing members of a struct
    ... int r, g, b; ... bool isDefined; ... Since you are posting from that broken usenet interface at google, ...
    (comp.lang.c)
  • Re: A problem with Math::Prime::XS
    ... [A complimentary Cc of this posting was sent to ... Tassilo v. Parseval ... long i, n, pcount, square_root; ... bool is_prime; ...
    (comp.lang.perl.modules)
  • Infinite loop when using Server.Transfer
    ... When I'm posting a page to itself with the bool value as true it falls into ... My problem is that this KB is saying that this problem should be solved with ... ServicePack 1 of .NET 1.1. ...
    (microsoft.public.dotnet.framework.aspnet)