[E-Lang] remote comms

Marc Stiegler marcs@skyhunter.com
Sun, 1 Apr 2001 18:25:03 -0700


> Our (speaking for Dean and myself) recommendations for using remote
> references equate to recreating a LiveRef from a SturdyRef at the
> beginning of every small-scale transaction.  We've hidden from the
> developers the problem of ensuring that there's a live connection to
> each server that a component relies on.  We've provided them with a
> single call that asks for a proxy for any particular service they rely
> on, and recommended that they re-use it throughout a transaction, but
> ask for a new copy when they receive a new request.
>
> One of the pieces we haven't implemented yet that these developers will
> definitely need is a simple invocation for time-out logic for
> practically all remote messages.  I think this is the same need that
> zooko is talking about with his impatience logic.  In our application,
> all (user-initiated) requests will have explicit requirements-driven
> need to timeout and return a default answer.  In our context (making
> access control decisions) the default answer is that if we haven't
> received the information to make a positive decision in a certain amount
> of time, we forbid access.  The distributed components are working
> together to reach this decision, so it could just be the code that talks
> to the user that times out.  We'll probably have time-outs in most of
> the components so they take a limited amount of time and don't waste
> computrons on hard questions that the requester has given up on.
>
> My intuition today is that it should be simple to build both promises
> that have an explicit time-out and ones that don't.  The latter
> shouldn't time out any sooner than the underlying mechanisms (e.g. TCP)
require.

Since this was the second comment that said, we need easy timeout (and other
impatience policies) at a higher level, I thought I'd give everyone the code
to implement a timeout at the programmer level.

It turns out not to have been quite as easy as I'd thought (I had allowed
myself to become confused about multi-vow when-catch behavior). But it is
still easy.

To get set up, I have created 2 functions.  promiseFirstResolved returns a
promise for the first resolution in a list of promises (so if the first
resolution is to break a promise, this propogates the break). And
promiseTimeout uses the E Timer object to smash the promise it returns,
after a specified interval. So if you give promiseFirstResolved a list of
promises that includes a timeout and a promise for something you want "in a
timely fashion", you either get the fulfillment of the promise within the
time period or you get a timeout broken promise.

Once you have set up these functions, a timed-out wait for an object is a
normal single-argument when-catch. The timeout user only needs to know how
to use promiseFirstResolved and promiseTimeout together to build tons of
impatient when-catch clauses.

I broke this into these pieces because you can reuse promiseFirstResolved to
set up arbitrarily complex impatience policies. Either replace the
promiseTimeout function with a complex function, or add additional
conditions to the list given to promiseFirstResolved, any one of which can
terminate the wait by smashing its promise.

Anyway, this is a nice piece, and I will probably include it in Walnut. Both
promiseFirstResolved and promiseTimeout are handy utilities for more than
just this one purpose. Of course, markm will probably be annoyed with this
since I didn't use a "thunk" in promiseFirstResolved :-) I'm just an old
FORTRAN hacker at the end of the day :-)

Code below. Not thoroughly tested :-)

--marcs



? def promiseFirstResolved(listOfPromises) :any {
>     var isFirst := true
>     def [firstPromise, firstResolver] := PromiseMaker()
>     for eachPromise in listOfPromises {
>         when (eachPromise) -> done(each) {
>             if (isFirst) {
>                 isFirst := false
>                 firstResolver resolve(each)
>             }
>         } catch err {
>             if (isFirst) {
>                 isFirst := false
>                 firstResolver smash(err)
>             }
>         }
>     }
>     firstPromise
> }
# value: <promiseFirstResolved>

? def promiseTimeout(secondsToWait) :any {
>     def [timeoutPromise, timeoutResolver] := PromiseMaker()
>     def timeout() {timeoutResolver smash("Timeout")}
>     <unsafe:org.erights.e.extern.timer.Timer> theTimer() after(
>         1000*secondsToWait,
>         timeout)
>     timeoutPromise
> }
# value: <promiseTimeout>

? def [marcPromise,marcResolver] := PromiseMaker()
# value: [<Eventual ref>, <Open Resolver>]

# Note this is just a when-catch with a list of promises in
promiseFirstResolved
? when (promiseFirstResolved([marcPromise, promiseTimeout(10)])) ->
done(result) {
>     println("promise fulfilled in time" + result)
> } catch err {println("promise broken: "+ err)}
?
#(wait 11 seconds, hit Enter in Elmer--in E it would go on the event queue,
no Enter required :-)
promise broken: <StringException: Timeout>