[e-lang] Fixing the sealed persistence protocol (was Newbie questions about persistence)

Kevin Reid kpreid at mac.com
Sat Sep 19 12:32:31 EDT 2009


On Sep 19, 2009, at 11:42, Mark Miller wrote:
> On Mon, Sep 14, 2009 at 3:43 PM, Kevin Reid <kpreid at mac.com> wrote:
>> IMO, ScopeSetup should directly get the sealer from the
>> PersistentKeyHolder, rather than having E-level code do it. There may
>> be a reason to have more configurability than that though.
>>
>> MarkM, is there any reason the persistence sealer should not be  
>> widely
>> available?
>
> An oversight. This is a good plan. Thomas, feel free to submit a
> patch. Kevin, feel free to make a commitment along the lines you
> explain above.

Commitment? Do you mean commit? I'm not sure what exactly you mean --  
that I should revise and commit Thomas's patch, or ...?

>> But as a workaround, you can define the proxy as you describe. I  
>> would
>> recommend making it part of the implementation of the oneShot, and  
>> not
>> exposing its existence to the user. (One way to do it would be to  
>> have
>> your oneShot serialize, not as its current state, but rather the slot
>> of the used flag: def uncall := [makeOneShot, "withFlagSlot",
>> [&used]]. Then the 'used' slot object serves as the un-duplicatable
>> object.
>>
>> But this seems like a kludge, and I think we should fix the
>> persistence protocol. Thanks for spotting this.
>>
>> MarkM, do you agree this is an excessively surprising problem?
>
> This is indeed excessively surprising. I'm not sure whether or not the
> slot solution is a kludge. The persistence of var-slots needs to be
> fixed regardless.

The slot solution is a kludge because:

- nontrivial objects may have state which is not naturally expressed  
*across upgrades* as one or more var slots.

- it means the object may be constructed with its internal shared with  
other parties: this could violate invariants.

- it is not a natural style to program in and creates additional  
visual noise in the program.

- the maker provides degrees of freedom which are unrelated to the  
actual application.

- the need for it IS unnecessarily surprising -- programmers will  
write accidentally insecure programs by not doing it.


Let's think about how to fix the persistence protocol.

What we want to achieve is that an object (henceforth referred to as  
the oneShot, even though this is general) may reveal a portrayal to  
the persistence subsystem, and thus be persistent, but with the  
guarantee that it will only be instantiated at most once in any given  
future vat incarnation.

The problem with just returning a portrayal in a sealed box from  
__optSealedDispatch is that any other object can proxy its  
__optSealedDispatch to the oneShot and thus revive as a duplicate of it.

So one fix would be to stuff the identity in the box and check it:

# from org.erights.e.extern.persist.initTimeMachine
def persistUncaller {
     to optUncall(obj) :nullOk[__Portrayal] {
         if (Ref.isNear(obj) &&
               pUnsealer.amplify(obj) =~ \
                 [[==(makeTraversalKey(obj)), portrayal]]) {
             return portrayal
         } else {
             return null
         }
     }
}

def oneShot {
     to __optSealedDispatch(brand) { switch (brand) {
         match ==persistBrand {
             return persistSealer.seal(makeTraversalKey(oneShot),
                                       [makeOneShot, ...])
         }
     }}
}

An oddball variation of this would be to put the object in the box and  
use it instead if the identities don't match: this would permit  
proxying __optSealedDispatch but change the behavior such that the  
proxying object is revived as the lone oneShot rather than a duplicate  
of it.

def persistUncaller {
     to optUncall(obj) :nullOk[__Portrayal] {
         if (Ref.isNear(obj) &&
               pUnsealer.amplify(obj) =~ [[objForIdentity,  
portrayal]]) {
             if (obj == objForIdentity) {
                 return portrayal
             } else {
                 return [__identityFunc, "run", objForIdentity]
             }
         } else {
             return null
         }
     }
}

def oneShot {
     to __optSealedDispatch(brand) { switch (brand) {
         match ==persistBrand {
             return persistSealer.seal(oneShot, [makeOneShot, ...])
         }
     }}
}


Also, I note that org.erights.e.extern.persist.initTimeMachine's  
persistUncaller is currently equivalent to makeAmplifier in  
org.erights.e.elib.serial.makeAnUncaller except for requiring nearness.

- Should all amplifying uncallers work in this same way? I have not  
considered the matter, but I would think this issue is likely to crop  
up similarly in other applications.

- The nearness restriction should be dropped and the uncall made  
through Ref.optSealedDispatch. If a Proxy-ref wants to be persistent,  
let it.

Given both of these changes, the persistUncaller is just  
makeAnUncaller.makeAmplifier(pUnsealer).

-- 
Kevin Reid                                  <http://switchb.org/kpreid/>






More information about the e-lang mailing list