Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a disconnected TCP socket be reliably detected using MsgWaitForMultipleObjects?

Twisted includes a reactor implemented on top of MsgWaitForMultipleObjects. Apparently the reactor has problems reliably noticing when a TCP connection ends, at least in the case where a peer sends some bytes and then quickly closes the connection. What seems to happen is:

  1. The reactor calls MsgWaitForMultipleObjects with some socket handles and QS_ALLINPUT.
  2. The call completes and indicates the handle for a socket in this state (that is, has bytes waiting to be read and has been closed by the peer) is active.
  3. The reactor dispatches this notification to the common TCP implementation.
  4. The TCP implementation reads the available bytes from the socket. There are some, they get delivered to application code.
  5. Control is returned to the reactor, which eventually calls MsgWaitForMultipleObjects again.
  6. MsgWaitForMultipleObjects never again indicates that the handle is active. The TCP implementation never gets to look at the socket again, so it can never detect that the connection is closed.

This makes it appear as though MsgWaitForMultipleObjects is an edge-triggered notification mechanism. The MSDN documentation says:

Waits until one or all of the specified objects are in the signaled state
or the time-out interval elapses.

This doesn't sound like edge-triggering. It sounds like level-triggering.

Is MsgWaitForMultipleObjects actually edge-triggered? Or is it level-triggered and this misbehavior is caused by some other aspect of its behavior?

Addendum The MSDN docs for WSAEventSelect explains what's going on here a bit more, including pointing out that FD_CLOSE is basically a one-off event. After its signaled once, you'll never get it again. This goes some way towards explaining why Twisted has this problem. I'm still interested to hear how to effectively use MsgWaitForMultipleObjects given this limitation, though.

like image 666
Jean-Paul Calderone Avatar asked Sep 29 '11 14:09

Jean-Paul Calderone


1 Answers

In order to use WSAEventSelect and differentiate activities, you need to call WSAEnumNetworkEvents. Make sure you're processing each event that was reported, not just the first.

WSAAsyncSelect makes it easy to determine the cause, and is often used together with MsgWaitForMultipleObjects.

So you might use WSAAsyncSelect instead of WSAEventSelect.

Also, I think you have a fundamental misunderstanding of the difference between edge-triggered and level-triggered. Your reasoning seems to be more related to auto-reset vs manual-reset events.

like image 158
Ben Voigt Avatar answered Sep 17 '22 22:09

Ben Voigt