Re: Newbie questions about persistence

Thomas Leonard <tal-v5nx5w6akNyLE8xUarVfuPLx9OUvmyODWmv/[email protected]>
Newsgroups gmane.comp.lang.e.general
Organization IT Innovation
Message-ID <[email protected]>
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-v5nx5w6akNyLE8xUarVfuPLx9OUvmyODWmv/[email protected]
http://www.it-innovation.soton.ac.uk
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.