Commit bb378738 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 64356821
...@@ -190,9 +190,10 @@ func (b *ZBucket) PDeactivate() { ...@@ -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 // XXX check if already activated
err := b.pyObject.PActivate(ctx) activated, err := b.pyObject.PActivate(ctx)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -31,8 +31,10 @@ type Object interface { ...@@ -31,8 +31,10 @@ type Object interface {
POid() zodb.Oid POid() zodb.Oid
PSerial() zodb.Tid PSerial() zodb.Tid
// PActivate brings object to live state.
// PActivate requests in-RAM object data to be present. //
// 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 // On successful return the object data is either the same as in the
// database or, if this data was previously modified by user of // database or, if this data was previously modified by user of
...@@ -40,7 +42,7 @@ type Object interface { ...@@ -40,7 +42,7 @@ type Object interface {
// //
// Object data must be accessed only after corresponding PActivate // Object data must be accessed only after corresponding PActivate
// call, which marks that object's data as being in use. // 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. // PDeactivate indicates that corresponding PActivate caller finished access to object's data.
// //
...@@ -51,15 +53,20 @@ type Object interface { ...@@ -51,15 +53,20 @@ type Object interface {
// Note that it is valid to have several concurrent uses of object // Note that it is valid to have several concurrent uses of object
// data, each protected with corresponding PActivate/PDeactivate pair: // data, each protected with corresponding PActivate/PDeactivate pair:
// As long as there is still any PActivate not yet compensated with // 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 // Besides exotic cases, the caller thus must not use object's data
// after PDeactivate call. // after PDeactivate call.
PDeactivate() // XXX + -> nuse ? PDeactivate() // XXX + -> nuse ?
// PInvalidate requests in-RAM object data to be discarded.
//
PInvalidate() // XXX must not be called while there are users. // 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. // PyObject is the interface that every in-RAM object representing Python ZODB object implements.
...@@ -68,6 +75,7 @@ type PyObject interface { ...@@ -68,6 +75,7 @@ type PyObject interface {
PyClass() pickle.Class // python class of this object PyClass() pickle.Class // python class of this object
PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__() PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__()
} }
// object is common base for in-RAM representation of ZODB object. // object is common base for in-RAM representation of ZODB object.
...@@ -75,6 +83,8 @@ type object struct { ...@@ -75,6 +83,8 @@ type object struct {
jar *Connection jar *Connection
oid zodb.Oid oid zodb.Oid
serial zodb.Tid serial zodb.Tid
refcnt int32
} }
func (obj *object) PJar() *Connection { return obj.jar } func (obj *object) PJar() *Connection { return obj.jar }
...@@ -87,6 +97,8 @@ type pyObject struct { ...@@ -87,6 +97,8 @@ type pyObject struct {
pyclass pickle.Class pyclass pickle.Class
pystate interface{} 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 } 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 ...@@ -226,7 +238,7 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
return nil, ctx.Err() // XXX err ctx return nil, ctx.Err() // XXX err ctx
case <-load.ready: case <-load.ready:
// XXX check pyclass match // 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 { ...@@ -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. func (obj *object) PDeactivate() {
// // XXX
// 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 { // PInvalidate
return nil // already loaded */
// 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) func (obj *object) pdeactivate() {
if err != nil { nuse := atomic.AddInt32(&obj.refcnt, -1)
return err 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 // complain pyclass changed
// (both ref and object data uses pyclass so it indeed can be different) // (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.serial = serial
pyobj.pystate = pystate 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