Re: Ref.whenResolved

"Mark S. Miller" <[email protected]>
Newsgroups gmane.comp.lang.e.general
Message-ID <[email protected]>
On Sun, Dec 5, 2010 at 8:55 AM, Thomas Leonard <[email protected]> wrote:

> Ref.whenResolved makes this guarantee:
>
>     * Should the reactor be invoked with a
>     * non-broken value (and therefore a fulfilled value), all earlier
> messages
>     * sent on ref before the whenResolved are guaranteed to have been
>     * successfully delivered.
>
> This seems excessive. For example, if you do this (to ensure that
> farRef is resolved):
>
>  when (farRef) -> { ... }
>
> it forces a network round-trip before the when block executes.
>

Yes, that's intentional for the "when" expression. I agree that it conflates
two separate issues together, and makes code that only cares about the
resolution pay for a round trip delay it might not care about. As a higher
level convenience, this bundling seems safer, as it better supports the
intuition "use promise pipelining to do optimistic data flow chaining, but
use the 'when' expression to turn data flow back into control flow, where
the success block only happens 'after' previous stuff on this reference."

If we did unbundle this, then IMO we'd still need a convenient way to say
"after this reference is fulfilled and all previously sent messages have
been delivered". My general heuristic regarding ordering is to provide the
programmer by default with the strongest ordering that they
might inadvertently expect and that we can cheaply-enough enforce,
especially if implementations will often accidentally obey this stronger
ordering anyway. To do less is to invite more bugs which happen rarely. The
limits of this are a bit more than E-order but still well short of causal
order.

Given the current primitives you can code your cheaper-but-weaker operation
as

def fastWhenResolved(ref, reactor) {
    if (Ref.isResolved(ref)) {
        return reactor <- (ref) # skips round trip
    } else {
        return Ref.whenResolved(ref, reactor)
    }
}



>
> And if you do:
>
>  when (def results := rcvr<-getData()) -> { ... }
>
> it transmits the result data over the network twice!
>

I don't follow you. How/why does the result get transmitted twice?



>
>
> While trying to simplify the causality traces, I added a
> Ref.whenResolved(reactor) instance method.


It can't be exposed in E as an instance method, since references don't have
their own methods. So if you provide this directly in Java, even in Java you
should expose as public only another static method on Ref, and then the
static method would internally do the polymorphic dispatch based on a
non-public instance method. That way, all the Java clients outside Ref would
use the static method just as the non-Java clients would. This helps us
prepare to switch from the current Java-based captp to Kevin's E-based captp
implementation.



> This does E.send(reactor,
> "run", resolution) as soon as the resolution is known. e.g. a NearRef
> does:
>
>    public Throwable whenResolved(Object reactor) {
>        return E.sendAllOnly(reactor, "run", new Object[] {myTarget});
>    }
>
> SwitchableRef and OldRemotePromise queue up reactors locally and call
> resolutionRef.whenResolved() once committed.
>

Yes, this does motivate why you want a new primitive rather than using
the fastWhenResolved pattern above -- your primitive will fire earlier in
these cases.



>
> In my previous example (invoking a simple increment() method in a
> remote vat), this reduces the number of turns from 11 to 6, makes the
> causality graph a simple chain (no fork), and only sends the result
> value over the network only once.
>
> Before, a deliverOp used four turns:
>
> 1. Receive CapTP deliverOp message:
>    Ref answer := E.send(target, verb, args)
>    E.send(answer, "__whenMoreResolved", rdr) # (tell the remote
> caller the result)
> 2. The actual method is invoked. answer resolves, enqueuing the
> __whenMoreResolved on the vat (now = false)
> 3. PendingDelivery does E.sendAll(result, "__whenMoreResolved") (now =
> true)
> 4. __whenMoreResolved does E.send(rdr, "run", self)
>
> Now, it uses two:
>
> 1. Receive CapTP deliverOp message:
>    Ref answer := E.send(target, verb, args)
>    answer.whenResolved(rdr) # (tell the remote caller the result)
> 2.  The actual method is invoked. answer resolves, enqueuing the return
> message
>

By "enqueuing the return message", do you mean the same "rdr <- (answer)"
message as sent in the first scenario?

I suppose the main (only?) thing I find jarring in your technique is the
extra bookkeeping within SwitchableRef needed to remember these reactors.
Since promises are presumed to be used much more often locally than
remotely, this extra bookkeeping for the local case seems unfortunate.
Perhaps my reluctance here is misplaced? Perhaps in the local case, the turn
reduction more than pays for the extra bookkeeping?


>
> I'm wondering whether it makes sense to do this always, not just when
> debugging?
>

I don't think it makes sense to do this just when debugging. We should
either do this always or never. Regarding the extra noise in the causeway
trace logs, the Causeway filtering logic *should* simplify this into the
equivalent display -- if the appropriate infrastructure files are unchecked
in the filter dialog. I say "*should*" because I don't know if Causeway
currently does. We should test on some actual logs. If Causeway doesn't,
this may simply call for some adjustment to Causeway's filtering logic.




>
> What was the rationale for requiring previous messages to be delivered
> before running the when block?
>

Above ordering heuristic.



> What is the "now" flag for for messages enqueued on a vat, and why
> does Vat.qSendMsg set it to false?
>

The equivalent question just came up as Tyler, Terry, and I were looking at
Waterken which has no equivalent flag or extra turn. I know I added it to
maintain some ordering guarantee, but I don't remember the specifics. I will
investigate.



>
> Thanks,
>
>
> --
> Dr Thomas Leonard        http://0install.net/
> GPG: 9242 9807 C985 3C07 44A6  8B9A AE07 8280 59A5 3CC1
> GPG: DA98 25AE CAD0 8975 7CDA  BD8E 0713 3F96 CA74 D8BA
>
> _______________________________________________
> e-lang mailing list
> [email protected]
> http://www.eros-os.org/mailman/listinfo/e-lang
>



-- 
    Cheers,
    --MarkM

_______________________________________________
e-lang mailing list
[email protected]
http://www.eros-os.org/mailman/listinfo/e-lang
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.