ERights Home elib / capability 
Back to: Capability Myths Demolished On to: Horton's "Who Done It?"

Dead-Man Switch


(This page shows only cooperative revocation. For an explanation of uncooperative revocation, see The Membrane Pattern.)

The distributed capability paradigm is surprising faithful to the properties of the single-machine capability paradigm. One of the differences between the two is that single-machine capabilities are "reliable", meaning that they work perfectly for as long as their universe exists. Distributed capabilities can be at most fail-stop, since one side can fail without the other, or a partition can separate them. Usually, this fails safe, since an inability to exercise authority is a lack of service, but not a breach. The revokable forwarder is a nice counter-example to this pleasant principle.

def makeRevoker(var myPrecious) :any {
    def revoker {
        to pass(verb, args) :any {
            E.send(myPrecious, verb, args)
        }
        to revoke(problem) {
            myPrecious := Ref.broken(problem)
        }
    }
    def forwarder {
        to __printOn(out) {
            myPrecious.__printOn(out)
        }
        match [verb, args] {
            revoker.pass(verb, args)
        }
    }
    [forwarder, revoker]
}

If the holder of the revoker is remote, then a partition can prevent them from sending a revoke message, leaving the holder of the wrapper with too much authority. To solve this, we turn the revoker into a "DeadManSwitch" as follows:

    def revoker {
        to pass(verb, args) :any {
            E.send(myPrecious, verb, args)
        }
        to revoke(problem) {
            # myPrecious <- __reactToLostClient(problem)   [see below]
            myPrecious := Ref.broken(problem)
        }
        to __reactToLostClient(problem) {
           revoker.revoke(problem)
        }
    }

__reactToLostClient is a MirandaMethod explained as:

When someone was holding a partitionable eventual reference to this object, and it suffers a partition, then this object is informed that one of its clients may no longer be able to talk to it, and why.

The Miranda behavior is to do nothing, but objects may override this to provide DeadManSwitch behavior. For example, a revoking facet of a revokable service may decide that if its client may no longer be able to talk to it, that it should auto-revoke. However inconvenient this solution, it is failsafe.

In practice, one would also add a __reactToLostClient method to the forwarder, to forward this message to myPrecious:
    def forwarder {
        to __printOn(out) {
            myPrecious.__printOn(out)
        }
        to __reactToLostClient(problem) {
           myPrecious.__reactToLostClient(problem)
        }
        match [verb, args] {
            revoker.pass(verb, args)
        }
    }

This allows a revocable forwarder to a DeadManSwitch to itself be a DeadManSwitch, whose ability to trigger the underlying DeadManSwitch is revocable.

This raises an interesting design choice: Should the revoker send a __reactToLostClient messages to the underlying object during a revoke(), in case the underlying is also a DeadManSwitch? How should we think about revoking rights to revoke? Should we consider this a loss to the underlying of a client?

 
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.
ERights Home elib / capability 
Back to: Capability Myths Demolished On to: Horton's "Who Done It?"
Download    FAQ    API    Mail Archive    Donate

report bug (including invalid html)

Golden Key Campaign Blue Ribbon Campaign