Substituting for TryAccept

We're back to the channel pump for another round. In the previous channel pump article we had introduced an asynchronous coroutine between the main channel pump loop and a callback on acquiring a channel throttle. This use of coroutines let us suspend the channel pump until a throttle was available without having to tie up a thread during that wait. That left us with the following channel pump code.

 ChannelPump
BEGIN LOOP
  AcceptChannel
   IF AcquireThrottle THEN
     DoWork (runs in the background)
 ELSE
        STOP (GotThrottle will run when a throttle is free)
END LOOP
 GotThrottle
DoWork (runs in the background)
ChannelPump
 DoWork
MessagePump
CloseChannel
ReleaseThrottle

Now, let's look at the problem of accepting a channel. In many cases, we could fail to actually get a channel when we call AcceptChannel. For example, the channel listener may have been closed, the timeout for listening may have expired, or the network may have gone down. Some of these conditions are transient and some indicate that no more channels will ever be created. What we really want to know after failing to get a channel is whether the channel pump should keep trying.

In a message pump, we can build the desired behavior by calling TryReceive on the channel. Unfortunately, there's no equivalent TryAcceptChannel. We'll have to build our own TryAcceptChannel to replace the call to AcceptChannel in ChannelPump. Under the covers, we'll call AcceptChannel inside of a try-catch block. The trick is to handle the exceptions properly and then return whether TryAcceptChannel succeeded in accepting a channel. Here's how TryAcceptChannel should handle each case.

  • AcceptChannel completes normally and returned a channel: success
  • AcceptChannel completes normally and returned null: success (read this article if you're unclear why)
  • AcceptChannel fails due to being aborted or faulted: success (read the same article above if you're unclear why)
  • AcceptChannel fails due to a timeout: retry
  • AcceptChannel fails due to some other reason: either retry or abort depending on whether the exception is recoverable

We can finish up by rewriting ChannelPump to take advantage of our new TryAcceptChannel method.

 ChannelPump
BEGIN LOOP
   IF TryAcceptChannel THEN
        IF channel IS NULL THEN
         STOP
        END IF
      IF AcquireThrottle THEN
         DoWork (runs in the background)
     ELSE
            STOP (GotThrottle will run when a throttle is free)
     END IF
  END IF
END LOOP
 GotThrottle
DoWork (runs in the background)
ChannelPump
 DoWork
MessagePump
CloseChannel
ReleaseThrottle

Next time: Checking for ServiceSecurityContext