Commit bb378738 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 64356821
......@@ -190,9 +190,10 @@ func (b *ZBucket) PDeactivate() {
}
func (b *ZBucket) PActivate(ctx context.Context) error {
// PActivate implements Object.
func (b *ZBucket) PActivate(ctx context.Context) (bool, error) {
// XXX check if already activated
err := b.pyObject.PActivate(ctx)
activated, err := b.pyObject.PActivate(ctx)
if err != nil {
return err
}
......
......@@ -31,8 +31,10 @@ type Object interface {
POid() zodb.Oid
PSerial() zodb.Tid
// PActivate requests in-RAM object data to be present.
// PActivate brings object to live state.
//
// It requests to persistency layer that in-RAM object data to be present.
// If object state is not in RAM - it is loaded from the database.
//
// On successful return the object data is either the same as in the
// database or, if this data was previously modified by user of
......@@ -40,7 +42,7 @@ type Object interface {
//
// Object data must be accessed only after corresponding PActivate
// call, which marks that object's data as being in use.
PActivate(ctx context.Context) error // XXX + -> nuse ?
PActivate(ctx context.Context) (activated bool, _ error) // XXX + -> nuse ?
// PDeactivate indicates that corresponding PActivate caller finished access to object's data.
//
......@@ -51,15 +53,20 @@ type Object interface {
// Note that it is valid to have several concurrent uses of object
// data, each protected with corresponding PActivate/PDeactivate pair:
// As long as there is still any PActivate not yet compensated with
// corresponding PDeactivate, object data will stay alive in RAM.
// corresponding PDeactivate, object data will assuredly stay alive in RAM.
//
// Besides exotic cases, the caller thus must not use object's data
// after PDeactivate call.
PDeactivate() // XXX + -> nuse ?
PInvalidate() // XXX must not be called while there are users.
// PInvalidate requests in-RAM object data to be discarded.
//
// Irregardless of whether in-RAM object data is the same as in the
// database, or it was modified, that in-RAM data must be forgotten.
//
// PInvalidate must not be called while there is any in-progress
// object's data use (PActivate / PDeactivate).
PInvalidate()
}
// PyObject is the interface that every in-RAM object representing Python ZODB object implements.
......@@ -68,6 +75,7 @@ type PyObject interface {
PyClass() pickle.Class // python class of this object
PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__()
}
// object is common base for in-RAM representation of ZODB object.
......@@ -75,6 +83,8 @@ type object struct {
jar *Connection
oid zodb.Oid
serial zodb.Tid
refcnt int32
}
func (obj *object) PJar() *Connection { return obj.jar }
......@@ -87,6 +97,8 @@ type pyObject struct {
pyclass pickle.Class
pystate interface{}
loaderr error // if there was error at state loading
ready chan struct{} // activation complete
}
func (pyobj *pyObject) PyClass() pickle.Class { return pyobj.pyclass }
......@@ -226,7 +238,7 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
return nil, ctx.Err() // XXX err ctx
case <-load.ready:
// XXX check pyclass match
return load.pyobj, err
return load.pyobj, load.err
}
}
......@@ -451,38 +463,96 @@ func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) PyObject {
}
// XXX pyobj.PInvalidate() = deactivate without checking if state != modified
// PDeactivates transforms object to ghost state.
//
// In ghost state object data is dropped and only oid/pyclass information is left in RAM.
func (pyobj *pyObject) PDeactivate() {
// FIXME if state=modified PDeactivate must be noop.
// ----------------------------------------
pyobj.pystate = nil
pyobj.serial = 0
/*
func (obj *object) PActivate(ctx context.Context) error {
// XXX
}
// PActivate brings object to live state.
//
// If object state is not in RAM - it is loaded from the database.
func (pyobj *pyObject) PActivate(ctx context.Context) error {
if pyobj.pystate != nil {
return nil // already loaded
func (obj *object) PDeactivate() {
// XXX
}
// PInvalidate
*/
// object's pactivate & friends that only manage base activation state, without actually loading data.
func (obj *object) pactivate(ctx context.Context) error {
nuse := atomic.AddInt32(&obj.refcnt, +1)
if nuse == 1 {
// we become responsible for loading object's data.
// XXX check .tryToKeepInRam
}
}
pyclass, pystate, serial, err := pyobj.jar.loadpy(ctx, pyobj.oid)
if err != nil {
return err
func (obj *object) pdeactivate() {
nuse := atomic.AddInt32(&obj.refcnt, -1)
if nuse < 0 {
panic("pdeactivate: nuse < 0")
}
if nuse > 0 {
return // users still left
}
// no users left. Let's see whether we should transition this object to ghost.
// TODO state=modified -> don't drop.
if drop {
obj.serial = 0
}
}
// PActivate implements Object.
func (pyobj *pyObject) PActivate(ctx context.Context) (bool, error) {
load := pyobj.object.pactivate()
if !load {
// someone else is already activated/activating the object.
// wait for its loading to complete and we are done.
select {
case <-ctx.Done:
return false, ctx.Err() // XXX err ctx
case <-pyobj.ready:
return (pyobj.loaderr == nil), pyobj.loaderr // XXX err ctx?
}
}
if pyclass != pyobj.pyclass {
// we become responsible for loading the object
pyclass, pystate, serial, err := pyobj.jar.loadpy(ctx, pyobj.oid)
if err == nil && pyclass != pyobj.pyclass {
// complain pyclass changed
// (both ref and object data uses pyclass so it indeed can be different)
return &wrongClassError{want: pyobj.pyclass, have: pyclass} // XXX + err ctx
err = &wrongClassError{want: pyobj.pyclass, have: pyclass} // XXX + err ctx
pystate = nil
}
pyobj.serial = serial
pyobj.pystate = pystate
return nil
pyobj.loaderr = err
close(pyobj.ready)
return err // XXX err ctx
}
// PDeactivate implements Object.
func (pyobj *pyObject) PDeactivate() {
drop := pyobj.object.pdeactivate()
if !drop {
return
}
// we have to drop pyobject state
// XXX locking?
pyobj.pystate = nil
pyobj.loaderr = nil
pyobj.ready = make(chan struct{})
return drop
}
// XXX pyobj.PInvalidate() = deactivate without checking if state != modified
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment