Sometimes you work with an API that returns a Choice. If you want to add a timeout to any choice we need to extend the set of extension methods I introduced in part 22 with this:

  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 WithTimeout(
 13:         this Choice choice, 
 14:         Handler<DateTime> timeoutHandler)
 15:     {
 16:         return choice.WithTimeout(timeoutHandler, DefaultTimeout);
 17:     }
 18:  
 19:     public static Choice WithTimeout(
 20:         this Choice choice, 
 21:         Handler<DateTime> timeoutHandler, 
 22:         TimeSpan timeout)
 23:     {
 24:         var choiceCompletedPort = new Port<EmptyValue>();
 25:         Arbiter.Activate(
 26:             taskQueue,
 27:             Arbiter.FromIteratorHandler(
 28:                 () => ExecuteChoice(choice, choiceCompletedPort)));
 29:         var timeoutPort = new Port<DateTime>();
 30:         taskQueue.EnqueueTimer(timeout, timeoutPort);
 31:  
 32:         return Arbiter.Choice(
 33:             choiceCompletedPort.Receive(), 
 34:             timeoutPort.Receive(dt => timeoutHandler(dt)));
 35:     }
 36:  
 37:     private static IEnumerator<ITask> ExecuteChoice(
 38:         Choice choice, 
 39:         Port<EmptyValue> port)
 40:     {
 41:         yield return choice;
 42:         port.Post(EmptyValue.SharedInstance);
 43:     }
 44: }

And the test to show how it works:

 45: [TestMethod]
 46: public void AddingTimeoutToChoice_Timeout()
 47: {
 48:     ChoiceWithTimeoutExtenstions.DefaultTimeout = TimeSpan.FromSeconds(5);
 49:     var port = new SuccessFailurePort();
 50:     Exception error = null;
 51:     bool successReported = false;
 52:     Arbiter.Activate(
 53:         dispatcherQueue, 
 54:         port.Choice(s => successReported = true, e => error = e)
 55:             .WithTimeout(_ => port.Post(new TimeoutException())));
 56:     Thread.Sleep(TimeSpan.FromSeconds(10));
 57:     Assert.IsNotNull(error);
 58:     Assert.IsInstanceOfType(error, typeof(TimeoutException));
 59:     Assert.IsFalse(successReported);
 60:     port.Post(SuccessResult.Instance);
 61:     Thread.Sleep(TimeSpan.FromSeconds(5));
 62:     Assert.IsFalse(successReported);
 63: }

And some example code:

 64: public static IEnumerator<ITask> TimeoutOnAnyChoice<T1, T2>(
 65:     PortSet<T1, T2> port)
 66: {
 67:     yield return
 68:         port.Choice(
 69:             a => Console.WriteLine("A: {0}", a), 
 70:             b => Console.WriteLine("B: {0}", b)).WithTimeout(
 71:                 _ => port.PostUnknownType(
 72:                     new TimeoutException("Timeout waiting for choice.")));
 73: }