[e-lang] Newbie questions about persistence
Thomas Leonard
tal at it-innovation.soton.ac.uk
Tue Sep 1 11:08:28 EDT 2009
On Fri, 2009-08-28 at 19:15 -0400, Kevin Reid wrote:
> On Aug 28, 2009, at 10:27, Thomas Leonard wrote:
[...]
> > Many things cannot be serialised by default, including:
> >
> > - facets (obj.method)
>
> This is a bug, IMO, and probably due to the lack of availability of
> the persistence sealer as discussed below.
Would be nice to have.
> > - near problems (bug in minimalUncaller?)
>
> Do you mean broken references? Could you provide details on this bug?
def makeAtomicFile := <import:org.erights.e.extern.persist.makeAtomicFile>
timeMachine.new()
timeMachine.createAs(makeAtomicFile(<file:.>, "tmp"))
introducer.onTheAir()
makeSturdyRef(Ref.broken("foo"))
timeMachine.save()
gives:
# problem: Can't uneval problem: foo
But, oddly:
def makeAnUncaller := <elib:serial.makeAnUncaller>
def minimalUncaller := makeAnUncaller.getMinimalUncallers()[0]
println(minimalUncaller.optUncall(Ref.broken("foo")))
gives:
[<makeRef>, "broken", [problem: foo]]
> > Persistence of sturdy refs
> >
> > A sturdy ref can only be serialised if fully resolved (is this a
> > bug?),
> This is a bug; I have committed a fix.
Thanks. There's a similar bug with files:
def makeAtomicFile := <import:org.erights.e.extern.persist.makeAtomicFile>
timeMachine.new()
timeMachine.createAs(makeAtomicFile(<file:.>, "tmp"))
introducer.onTheAir()
def a
bind a := <file:foo.txt>
makeSturdyRef(a)
timeMachine.save()
gives:
# problem: Can't uneval <file:.../foo.txt>
> > Persistence of cycles
> >
> > Some cyclic data structures can be persisted, while others can't (is
> > this a bug?). I haven't worked out what the rule is, but I do have
> > some examples:
> ...
> > Serializing [[<***CYCLE***>], [<***CYCLE***>]]...
> > Serialized as de: [def t__0 := [t__0], t__0]
> > # problem: <IndexOutOfBoundsException: not found: t__0>
> >
> > This is quite annoying, because a SwissRetainer includes a pointer to
> > its object, in addition to the pointer in the table for which the
> > retainer is a key. Therefore, an object containing a single cycle ends
> > up with the broken double-cycle structure when saved.
>
> This is a bug. Please send versions, test cases, patches, etc.
A minimal test case is:
def surgeon := <elib:serial.makeSurgeon>.withSrcKit("de: ")
surgeon.unserialize("de: [def t__0 := [t__0], t__0]")
which gives:
# problem: <IndexOutOfBoundsException: not found: t__0>
Happens with the current release (0.9.2a) and svn versions of E.
> > Persistence of identity
[...]
> > Therefore, most objects should use __optSealedDispatch to seal the
> > result. There don't seem to be many examples of this (e.g. FileGetter
> > uses "obj instanceof File" instead). I'm having trouble seeing how to
> > use this. Should I add a new loader/uncaller?
>
> No, you should use the vat-wide persistence sealer, whose unsealer is
> closely held by the vat persistence system, in your
> __optSealedDispatch. If you seal your portrayal (what you would return
> from __optUncall) then you will get the behavior you want without any
> global definitions.
>
> Unfortunately, IIRC, the persistence sealer is also closely held, but
> there is no good reason for this. (MarkM, could you confirm this?)
>
> > How is identity handled?
> > e.g if I have a one-shot object, how can I ensure that an object
> > holding it will revive with only one copy of the one-shot object?
> > Similarly for a caretaker.
>
> If your object uncalls only by request of the vat persistence sealer,
> then you're guaranteed that its reincarnations will only be along with
> the rest of the vat state. So your one-shot might be rolled back to
> the not-yet-fired state, but everything else will be rolled back too
> and in sync.
I think I still need more help here. A naive attempt would be:
def makeOneShot(var used) {
return def oneShot {
to __optSealedDispatch(brand) {
if (keyHolder.getTHE_BRAND() == brand) {
def uncall := [makeOneShot, "run", [used]]
return keyHolder.getTHE_SEALER().seal(uncall)
}
return null
}
to invoke() {
if (used) {
throw("Used up")
}
used := true
println("Invoking!")
}
}
}
The idea here is that a oneShot can be used to print "Invoking!" only
once, e.g.
def myOneShot := makeOneShot(false)
myOneShot.invoke()
myOneShot.invoke()
gives:
Invoking!
# problem: Used up
But, since anyone can call __optSealedDispatch, an attacker can
obviously make copies of my oneShot. e.g.
def makeMimic(obj) {
return def mimic {
to __optSealedDispatch(brand) {
return obj.__optSealedDispatch(brand)
}
}
}
def makeAttacker {
to run(oneShot) {
return def larva {
to __optUncall() {
return [makeAttacker, "revive", [oneShot, makeMimic(oneShot)]]
}
}
}
to revive(oneShotA, oneShotB) {
oneShotA<-invoke()
oneShotB<-invoke()
return def obj { }
}
}
My attacker gets persisted with a single oneShot, but revives with two
(and can therefore print "Invoking!" twice).
I suppose I could create a simple proxy to the oneShot and give that to
the attacker. The attacker could make copies of the proxy, but they'd
still all forward to a single oneShot. It seems clumsy and error-prone,
though, and I'm not sure I've thought through all the implications. A
simple example to copy from would be good.
Thanks,
--
Dr Thomas Leonard
IT Innovation Centre
2 Venture Road
Southampton
Hampshire SO16 7NP
Tel: +44 0 23 8076 0834
Fax: +44 0 23 8076 0833
mailto:tal at it-innovation.soton.ac.uk
http://www.it-innovation.soton.ac.uk
More information about the e-lang
mailing list