Udostępnij za pośrednictwem


CCR tips and tricks - part 22

As mentioned in part 11 there is a good idea to use timeouts when waiting for a result on a result port. But if you're going to do it in a lot of places you want to have a helper to deal with this. Here is a class that adds extension methods that are similar to the regular Choice extension method but that also adds a timeout.

   1: public static class ChoiceWithTimeoutExtenstions
  2: {
  3:     private static DispatcherQueue taskQueue = new DispatcherQueue();
  4:  
  5:     public static TimeSpan DefaultTimeout { get; set; }
  6:  
  7:     static ChoiceWithTimeoutExtenstions()
  8:     {
  9:         DefaultTimeout = TimeSpan.FromSeconds(30);
 10:     }
 11:  
 12:     public static Choice ChoiceWithTimeout<T>(
 13:         this PortSet<T, Exception> portSet,
 14:         Handler<T> successHandler,
 15:         Handler<Exception> errorHandler)
 16:     {
 17:         return portSet.ChoiceWithTimeout(
 18:             successHandler, 

 19:             errorHandler, 
 20:             DefaultTimeout);
 21:     }
 22:  
 23:     public static Choice ChoiceWithTimeout<T>(
 24:         this PortSet<T, Exception> portSet,
 25:         Handler<T> successHandler,
 26:         Handler<Exception> errorHandler,
 27:         TimeSpan timeout)
 28:     {
 29:         var timeoutPort = new Port<DateTime>();
 30:         Arbiter.Activate(
 31:             taskQueue,
 32:             timeoutPort.Receive(
 33:                 _ => portSet.Post(
 34:                     new TimeoutException("Timeout waiting for choice."))));
 35:         taskQueue.EnqueueTimer(timeout, timeoutPort);
 36:         return portSet.Choice(successHandler, errorHandler);
 37:     }
 38:  
 39:     public static Choice ChoiceWithTimeout<T>(
 40:         this PortSet<T, Exception> portSet,
 41:         Handler<T> successHandler,
 42:         Handler<Exception> errorHandler,
 43:         Handler<DateTime> timeoutHandler)
 44:     {
 45:         return portSet.ChoiceWithTimeout(
 46:             successHandler, 
 47:             errorHandler, 
 48:             timeoutHandler, 
 49:             DefaultTimeout);
 50:     }
 51:  
 52:     public static Choice ChoiceWithTimeout<T>(
 53:         this PortSet<T, Exception> portSet,
 54:         Handler<T> successHandler,
 55:         Handler<Exception> errorHandler,
 56:         Handler<DateTime> timeoutHandler,
 57:         TimeSpan timeout)
 58:     {
 59:         var timeoutPort = new Port<DateTime>();
 60:         taskQueue.EnqueueTimer(timeout, timeoutPort);
 61:         return Arbiter.Choice(
 62:             portSet.Receive(successHandler), 
 63:             portSet.Receive(errorHandler), 
 64:             timeoutPort.Receive(timeoutHandler));
 65:     }
 66: }

Here is a test showing how it works:

  67: [TestMethod]
 68: public void BasicWay_Timeout()
 69: {
 70:     ChoiceWithTimeoutExtenstions.DefaultTimeout = TimeSpan.FromSeconds(5);
 71:     var port = new SuccessFailurePort();
 72:     Exception error = null;
 73:     bool successReported = false;
 74:     Arbiter.Activate(
 75:         dispatcherQueue, 
 76:         port.ChoiceWithTimeout(s => successReported = true, e => error = e));
 77:     Thread.Sleep(TimeSpan.FromSeconds(10));
 78:     Assert.IsNotNull(error);
 79:     Assert.IsInstanceOfType(error, typeof(TimeoutException));
 80:     Assert.IsFalse(successReported);
 81:     port.Post(SuccessResult.Instance);
 82:     Thread.Sleep(TimeSpan.FromSeconds(5));
 83:     Assert.IsFalse(successReported);
 84: }

And this is an example of usage:

  85: public static IEnumerator<ITask> TypicelUse(SuccessFailurePort port)
 86: {
 87:     yield return
 88:         port.ChoiceWithTimeout(
 89:             s => Console.WriteLine("Success: {0}", s.StatusMessage), 
 90:             e => Console.WriteLine("Failure: {0}", e));
 91: }