Timeout, the ability to execute an expression for at most a specified
amount of time, is an essential ingredient of fault-tolerant and distributed
programming. Orc accomplishes timeout using pruning and the Rwait
site.
The following program runs F for at most one second, publishing its result if
available and the value 0
otherwise.
Let( F | Rwait(1000) >> 0 )
In the auction example given previously, the auction may never complete if one of the bidders does not respond. We can add a timeout so that a bidder has at most 8 seconds to provide a bid:
def auction([]) = 0 def auction(b:bs) = val bid = b.ask() | Rwait(8000) >> 0 max(bid, auction(bs))
This version of the auction is guaranteed to complete within 8 seconds.
Sometimes, rather than just yielding a default value, we would like to
determine whether an expression has timed out, and if so, perform some other
computation. To detect the timeout, we pair the result of the original
expression with true
and the result of the timer with false
.
Thus, if the expression does time out, then we can distinguish that case
using the boolean value.
Here, we run expression F with a time limit t
. If it publishes
within the time limit, we bind its result to r
and execute G.
Otherwise, we execute H.
val (r, b) = (F, true) | (Rwait(t), false) if b then G else H
Instead of using a boolean and conditional, we could use pattern matching:
val s = Some(F) | Rwait(t) >> None() s >Some(r)> G | s >None()> H
It is even possible to encapsulate timeout as a function.
def timeout(x, t) = Let(Some(x) | Rwait(t) >> None())
timeout(F, t)
waits
t
milliseconds for F to publish a value. If F publishes v
within
the time limit, timeout
returns Some(v)
. Otherwise, it returns
None()
when the time limit is reached.
We can also apply timeout to streams. Let's
define a modified version of the repeat
function as follows:
def repeatWithTimeout(f, t) = timeout(f(), t) >Some(x)> (x | repeatWithTimeout(f, t))
We call f()
as before, but apply a timeout of t
to the call.
If a value becomes available from f
before the timeout, then the call to
timeout
publishes Some(x)
, which we match, and then publish
x
and recursively wait for further values from the stream.
However, if no value is available from f
within the timeout, the call
to timeout
publishes None()
. Since None()
does
not match the pattern, the entire expression halts, indicating that the end of the
stream has been reached.
It is also possible to achieve this behavior with the existing repeat
function,
simply by changing the function passed to repeat
:
def f'() = timeout(f(), t) >Some(x)> x repeat(f')