CCR tips and tricks - part 23
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: }