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: }