# Copyright 2005-2007 Kevin Reid, under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ................ User-defined Far refs and remote promises ? pragma.syntax("0.9") ? def makeProxy := # value: --- Far Ref ? var i := 0 > def exampleBrand {} > null ? def farHandler { > to handleSend(verb, args) :any { > println(`handleSend $verb $args`) > return [i += 1, verb, args] > } > to handleSendOnly(verb, args) :any { > println(`handleSendOnly $verb $args`) > return [i += 1, verb, args] # should not be returned from the proxy > } > to handleOptSealedDispatch(brand) { > println(`handleOptSealedDispatch $brand`) > if (brand == exampleBrand) { > return def brandResult {} > } > } > } # value: The second argument to makeProxy is the resolution of a remote promise, or the failure of a Far reference; the third is whether the proxy is resolved. These components together completely define the identity of a proxy. ? def remote := makeProxy(farHandler, (def resolution; resolution), true) # value: ? remote.hi() # problem: not synchronously callable: .hi() ? pragma.enable("accumulator") > accum [] for verb in ["isBroken", "isNear", "isEventual", "isFar", "isSettled", "isResolved", "optProblem"] { _.with(E.call(Ref, verb, [remote])) } # value: [false, false, true, true, true, true, null] ? def remoteKey := __equalizer.makeTraversalKey(remote) # value: -- Send on Far ref ? def firstResponse := remote <- first() # value: # stdout: handleSend first [] # Notice that even though the handler is written to resolve it immediately, the response remains a promise during the first turn. This is done deliberately so that E-implemented proxies cannot have synchronous effects during an E.send. ? interp.waitAtTop(firstResponse) ? Ref.fulfillment(firstResponse) # value: [1, "first", []] -- Send-only ? E.sendOnly(remote, "only", []) # stdout: handleSendOnly only [] # -- Breaking Far ref ? [Ref.optProblem(remote), > resolution__Resolver.resolve(def x := Ref.broken("gone away"); &x), > Ref.optProblem(remote)] # value: [null, null, problem: gone away] ? remote <- msg() # value: confirm sameness as best we can ? __equalizer.makeTraversalKey(remote) == remoteKey # value: true (The synchronous visibility of <- on a broken reference is a separate issue, and works the same way for Ref.broken/1) --- Remote Promise ? def promiseHandler {} # value: ? def remotePromise := makeProxy(promiseHandler, (def resolution; resolution), false) # value: ? remotePromise.hi() # problem: not synchronously callable: .hi() ? pragma.enable("accumulator") > accum [] for verb in ["isBroken", "isNear", "isEventual", "isFar", "isSettled", "isResolved", "optProblem"] { _.with(E.call(Ref, verb, [remotePromise])) } # value: [false, false, true, false, false, false, null] ? resolution__Resolver.resolve(def promiseResolution {}; &promiseResolution) ? remotePromise # value: ? remotePromise == promiseResolution # value: true ? accum [] for verb in ["isBroken", "isNear", "isEventual", "isFar", "isSettled", "isResolved", "optProblem"] { _.with(E.call(Ref, verb, [remotePromise])) } # value: [false, true, false, false, true, true, null] --- Promise resolution cases The current implementation checks for the resolution promise becoming resolved each time the proxy is used in some way, and converts it into a forwarding ref upon that resolution. This section tests such usage. ? def makeJustResolved(value) :any { > def r > def p := makeProxy(promiseHandler, r, false) > bind r := __makeFinalSlot(value) > return p > }; null ? makeJustResolved(1).add(2) # value: 3 ? Ref.state(makeJustResolved(1)) # value: "NEAR" ? Ref.state(makeJustResolved(Ref.broken("fb"))) # value: "BROKEN" XXX what does this test have to do with its surroundings? ? Ref.optSealedDispatch(def _ match msg { msg }, 1) # value: ["__optSealedDispatch", [1]] ? interp.waitAtTop(makeJustResolved(println) <- ("ok")) # stdout: ok # ? E.sendOnly(def p := makeJustResolved(println), "run", ["ok2"]) > interp.waitAtTop(p <- __getAllegedType()) # stdout: ok2 # --- Sameness of Far refs ? def stubHandler {} # value: ? def stubResolution := Ref.promise()[0] # value: different proxies, same identity ? makeProxy(def fi1 {}, stubResolution, true) == makeProxy(fi1, stubResolution, true) # value: true different proxies, different identity ? makeProxy(fi1, stubResolution, true) == makeProxy(def fi2 {}, stubResolution, true) # value: false different proxies, different instantiation of same Selfless identity ? makeProxy([fi1], stubResolution, true) == makeProxy([fi1], stubResolution, true) # value: true unresolved one ? makeProxy(stubHandler, stubResolution, false) == makeProxy(stubHandler, stubResolution, true) # problem: == > unresolved both ? makeProxy(stubHandler, stubResolution, false) == makeProxy(stubHandler, stubResolution, false) # problem: == > unresolved-but-same ? (def pr := makeProxy(stubHandler, stubResolution, false)) == pr # value: true Becoming-same resolutions: A Far ref must not change its identity, as it is resolved. Therefore, a Far ref's identity is not based on the resolution promise but rather the traversal-key of the promise at the time of proxy creation. ? def p1 := makeProxy(stubHandler, (def r1;r1), true) > def p2 := makeProxy(stubHandler, (def r2;r2), true) > p1 == p2 # value: false ? bind r2 := r1; null ? p1 == p2 # value: false -- this is what we're checking for --- Sameness of broken Far references ? def p1 := makeProxy(def sbfh {}, (def sbfr;sbfr), true) # value: ? def p2 := makeProxy(sbfh, sbfr, true) # value: ? def pO := makeProxy(def sbfh2 {}, (def sbfr2;sbfr2), true) # value: ? p1 == p2 # value: true ? p1 == pO # value: false ? def p := Ref.broken("yellow") > for x in [sbfr__Resolver, sbfr2__Resolver] { x.resolve(&p) } ? p1 # value: ? p1 == p2 # value: true ? p1 == pO # value: false --- Failure cases Unsettled handler ? makeProxy(Ref.promise(), Ref.promise()[0], false) # problem: , ]> Bad resolution box ? { def remote := makeProxy(stubHandler, (def resolution; resolution), false) > bind resolution := false > remote() } # problem: Resolution promise of a proxy handled by didn't resolve to a simple slot, but false. ? { def remote := makeProxy(stubHandler, (def resolution; resolution), true) > bind resolution := false > remote() } # problem: Resolution promise of a proxy handled by didn't resolve to a simple slot, but false. Resolution identity conflict ? { def remote := makeProxy(stubHandler, (def resolution; resolution), true) > bind resolution := &false > remote() } # problem: Attempt to resolve a Far ref handled by to another identity (false). --- optSealedDispatch ? [Ref.optSealedDispatch(remote, exampleBrand)] # value: [null] ? [Ref.optSealedDispatch(makeProxy(farHandler, Ref.promise()[0], true), exampleBrand)] # stdout: handleOptSealedDispatch # # value: [] ? [Ref.optSealedDispatch(makeProxy(farHandler, Ref.promise()[0], false), exampleBrand)] # stdout: handleOptSealedDispatch # # value: [] XXX explicitly test hash code is preserved across breakage, so hash tables/TraversalKey etc. work XXX resolution to near/other-promise targets