[Persistence-sig] "Straw Baby" Persistence API

Phillip J. Eby pje@telecommunity.com
Tue, 23 Jul 2002 12:00:11 -0400


At 11:37 AM 7/23/02 -0400, Kevin Jacobs wrote:
>On Tue, 23 Jul 2002, Phillip J. Eby wrote:
>
> > So, you're saying you want to alter the types, then?  The interesting part
> > of that is how to alter them in such a way that your observing code 
> doesn't
> > get re-entered when you're modifying both subclasses and base classes of
> > the objects.  You'd need some kind of thread-specific collaboration stack,
> > I think.
>
>I suppose, though saying 'alter the types' implies slightly different things

I mean, if you're proxying methods, presumably you're doing so by altering 
the methods provided by the type, unless you mean to change the type's type 
so that the methods are altered on the fly.  Either way, a change to the 
type instance.  :)


>to me.  I don't see great difficulty in isolating subclass and superclass
>modifications, although performance is clearly an important issue.  As for
>the thread-specific business, you've totally lost me.  Can you provide a
>use-case so that I can better understand where you are coming from?

Consider a co-operative method that performs a super() call.  If one 
surrounds both the super and subclass with observer calls, they will take 
place more than once.  Perhaps that's what you mean by performance; I 
suppose if you are strictly observing things, it may not be a big deal to 
have the methods called more than once.

My comment about thread-specificness was about a way to ensure that the 
wrapper method only gets called once.  It's not relevant if you don't plan 
to ensure that wrappers on co-operative methods are called only once.

I should note, however, that there is one possibly rather important use 
case for not calling a wrapper more than once: object changes.  Let's say 
that class B is a subclass of class A.  B had an invariant that attribute 
"q" is always 3 times attribute "r", and has a setR() method that sets 
"r".  It uses a super() call to class A to do the actual setting of R, and 
then sets the "q" attribute.  Now, if there is a post-return observer 
associated with the setR() method in both A and B, it will be called at a 
point where it will announce a state that is valid for objects of type A, 
but violates an invariant for the specific instance being 
announced.  (Also, even if you didn't care about publishing an invalid 
state, it should be noted that use cases like Tim Peters' example of a 
distributed cache would really multiply the performance issue, especially 
if you're talking about a deep hierarchy of super() calls.)

Anyway, if we're strictly talking about observers, the simplest way to 
address this might be to carry a per-instance "nesting count" that you 
increment on entry to every proxy and decrement on exit.   When the count 
reaches zero on exit, fire any pending observation events.  Downside to 
this approach: if multiple threads enter overlapping calls on the object, a 
sort of "livelock" can occur where the object never issues any events.  To 
address that, you would have to have at least per-thread counters for each 
instance, which adds some more performance overhead for access.  This is 
where my comment about thread-specific collaboration stacks came from.