[E-Lang] New Page: Partially Ordered Message Delivery

Mark S. Miller markm@caplet.com
Mon, 26 Feb 2001 16:14:22 -0800


The context is the semantics documented at 
http://www.erights.org/elib/concurrency/partial-order.html , and in 
particular to the guarantee (in the three party case) that Alice is giving 
to Bob access only to post-X Carol.  I recommend looking at the diagrams on 
this page while reading the following.


At 03:14 AM Wednesday 2/14/01, Tyler Close wrote:
>Markm wrote:
>> Above, you ask the correct hard question: how do NEAR
>> references, which
>> allow synchronous invocation, screw this up?  It turns out,
>> in only the way
>> documented on that page:
>>
>> [after Alice eventually sends a message to Carol]
>> >If Alice and Carol are in the same Vat and Alice has a
>> NEAR reference to
>> >Carol, Alice can still immediately call Carol, perhaps
>> delivering a message
>> >before messages which were eventually sent earlier.
>>
>> Although this is ugly, it doesn't violate the above guarantee.
>
>Sure it does. The reference, from the point of view of Alice, had the
>ability to send immediate invocations. If Alice sends this reference
>to Bob in the same Vat, then Alice expects that Bob receives an
>equally powerful reference immediately upon receipt of the message.
>Alice is granting Bob access to pre-X Carol (since Alice cannot
>control when Carol gets X).

To see that the guarantees I have in mind do hold, let's separate this into 
four separate scenarios.


1) The scenario of mine you quote above, in which *Alice* does a call to 
Carol after she does a send to Carol.  What she has is a NEAR reference, 
which does enable her to call Carol.  So although it's admittedly ugly, the 
out-of-order arrival is defensible.


2) Your transform of this scenario, in which Alice eventually sends (<-) 
this reference to Bob in message Y after she has eventually sent X to Carol. 
The semantics guarantee that Bob will not be able to use this reference to 
deliver W before X.  Since a NEAR reference would give Bob the ability to 
deliver W immediately (via a call), the semantics require that the reference 
delivered to Bob not be NEAR until after X has been delivered.

The current implementation achieves this by not delivering Y to Bob until 
after X has been delivered to Carol. Therefore, the Y Bob receives can, 
without loss of correctness, contain a NEAR reference to Carol.  Btw, the 
current implementation achieves this for free simply by servicing the 
delivery queue in a FIFO manner, though the semantics do not demand this.


3) If Alice *calls* Bob with Y, then Bob receives the same reference to 
Carol that Alice provided -- a NEAR reference.  Bob can now call on this 
just as Alice could have, and so deliver a message to Carol ahead of X.  
Although Alice and Bob are now synchronously sharing a NEAR reference, and 
so don't have the guarantee regarding eventually sent messages, they do have 
a corresponding guarantee regarding immediate calls: If Alice called Carol 
with X before Alice called Bob with Y, then when Bob calls Carol with W, 
simple time sequence ensures that W is delivered to Carol after X.


4) In this variation on #3, Alice eventually sends X to Carol, but then she 
wishes to call Bob with the Y message containing only a post-X Carol 
argument.  She can still do so!

    define alice {
        to doSomething() {
            myCarol <- X
            myBob <- Y(myCarol <- yourself)
        }
    }

In response to the "yourself" message, by convention (provided by a Miranda 
method 

http://www.erights.org/javadoc/org/erights/e/elib/prim/MirandaMethods.html#yourself(java.lang.Object)

), an object simply returns itself.  This means "myCarol <- yourself" 
returns a promise for Carol, but that it will not resolve to Carol, nor will 
it deliver messages sent on this promise to Carol, until "yourself" itself 
gets delivered, which will not happen until all messages ahead of it (like 
X) get delivered.

In this scenario, we see that Bob's effective reference to Carol stays 
EVENTUAL until X is delivered to Carol, after which it turns NEAR.


>Now, if you take the exact same E program, and instead put Alice in a
>different Vat, why should you expect the authority transfer to change
>to post-X Carol?

Referring only to case #3 where it's an issue, if Carol is in a different 
Vat then Alice only had the ability to eventually send to her, not to call 
her.  For Alice to immediately call Bob, Bob must be in the same vat as 
Alice, in which case, as in #3, Alice can only share with Bob the access to 
Carol that she's got.


>> But what about the case you ask about?  Let's say that Bob
>> and Carol are in
>> the same Vat, whether or not Alice is in this Vat as well.
>
>It's the case where Alice is also in the same Vat that the authority
>flow is different.

#3 is different because, not only are all in the same Vat, but Alice is 
immediately calling Bob.  Other than that, the authority flow is all the same.

>> >The only way I can see to rescue this behaviour is to
>> attach semantics
>> >to the Vat.
>>
>> Do you agree that the above spec rescues the guarantee
>> without attaching
>> extra semantics to the Vat?
>
>Nope. The only thing saving the Alice, Bob and Carol in a Vat scenario
>is the implicit assumption that the message to Carol will be delivered
>before the message to Bob. You haven't turned this assumption into a
>guarantee.

The implementation must correctly implement the semantics.  Correct programs 
written in the language may only count the semantics and may not make 
assumptions about the implementation, since the program is supposed to be 
independent of any particular implementation.  However, the implementation 
may make assumptions about itself, since it is specific to itself.

The language semantics guarantee that an eventually sent Y forks the 
references it contains, and that a W sent on these forked references will 
not arrive before X.  The semantics indeed do not guarantee that X will be 
delivered to Carol before Y is delivered to Bob, and a correct program may 
not make such an assumption.  However, an implementation for which this 
assumption is true may correctly count on this assumption within the 
implementation.


>> When we tried to build it without
>> these ordering
>> guarantees, we had a hell of a time.
>>
>> The ordering guarantees originated in syntactic sugar used
>> in the Joule user
>> language (though I don't think they are in the on-line
>> manual).  When we
>> added these guarantees to Original-E and used them,
>> suddenly a whole mess of
>> n-party (for n >= 3) concurrency problems went away.
>
>Are these problems adequately solved by the multi-promise resolve
>sugar you recently introduced?

No.  They solve a different issue -- waiting for multiple separate things to 
be resolved before taking some action.


>[...] In Droplets, the
>Carol reference is just another sturdy reference that lives for as
>long as the Bob Vat is willing to let it live. This makes surviving
>temporary partitions a lot easier.
>
>In E, the advent of partition means that you have to tear down the
>entire structure of "live" references that you've built up and rebuild
>it from scratch when the partition is fixed. In Droplets, you may get
>a few smashed return values; however, you might be able to carry on as
>you were when the partition is fixed. Your pointers may still be good.
>E pointers that suffered a smashed return value are permanently
>broken.

In some sense, the whole point of the E mechanism is to "tear down the 
entire structure of 'live' references that you've built up" and make the 
programmer "rebuild it from scratch when the partition is fixed."  Rather 
than have all references be somewhat flaky all the time, and make the 
programmer have to deal with this flakiness everywhere, make all normal live 
use really reliable until it can't be, at which point everything's safely 
fail-stop.  Let the fine grained parts of computation built up with the fine 
grained structure of live remote reference become safely useless together, 
to be replaced by a new fine grained structure rebuilt from a small number 
of SturdyRefs.

This reproduces the valuable part of distributed atomic transactions -- 
abort (throwing away questionable state) and recovery (rebuilding from 
relatively stable things) -- while not taking on any impossible problems. 
The alternative of trying to pervasively maintain consistency in place seems 
too hard in one way, while the alternative of full distributed atomic 
transactions is simply impossible, because of the coordinated attack 
problem.  This middle point isn't even a compromise.  It's the sweet spot in 
the design space between these problematic poles.


>I'm reminded of a time when I was at your house and you were using a
>Web site over Ricochet. You disconnected from Ricochet and then
>reconnected using your land line. You then stopped and remarked on how
>wonderful it was that you could continue using the Web site without
>loss of session state. An E application would not be able to exhibit
>this same functionality. A Droplets application may.
>
>The Internet being what it is, I think the ability to easily handle
>temporary partitions is an important feature.

I agree that handling partitions is important.  That's the point!  I claim 
that the effort needed to correctly maintain consistency despite partitions 
is lower with the E pattern than with the Droplets pattern.  With less than 
this effort, the path of least resistance using the E pattern leads to an 
application that safely disconnects.  But using the Droplets pattern, the 
corresponding path of least resistance leads to an application that 
innocently continues in a confused state.



        Cheers,
        --MarkM