4.12. Timeout

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 )

4.12.1. Auction with timeout

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.

4.12.2. Detecting timeout

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.

Timeout streams

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) 
  (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