[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