Commit f532e0f2 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 9605b703
......@@ -86,12 +86,6 @@ type Object interface {
PInvalidate()
}
// XXX
type Stateful interface {
// XXX
DropState()
}
// PyObject is the interface that every in-RAM object representing Python ZODB object implements.
type PyObject interface {
Object
......@@ -99,21 +93,21 @@ type PyObject interface {
PyClass() pickle.Class // python class of this object
// PyState() interface{} // object state. python passes this to pyclass.__new__().__setstate__()
PyStateful
// XXX want to hide from PyObject. Rationale: we do not want e.g. PySetState to
// be available to user who holds PyObject interface: it is confusing to have
// both PActivate and PySetState at the same time.
// PyStateful
}
// XXX
type PyStateful interface {
Stateful
// PySetState should set Python state of the in-RAM object.
// Analog of __setstate__() in Python.
PySetState(pystate interface{}) error
// ObjectState describe state of in-RAM object.
type ObjectState int
// PyGetState should return Python state of in-RAM object.
// Analog of __getstate__() in Python.
//PyGetState() interface{} XXX
}
const (
GHOST ObjectState = -1
UPTODATE = 0
CHANGED = 1
// no STICKY - we pin objects in RAM with PActivate
)
// object is common base for in-RAM implementations of ZODB objects.
type object struct {
......@@ -121,6 +115,8 @@ type object struct {
oid zodb.Oid
serial zodb.Tid
mu sync.Mutex
state ObjectState
refcnt int32
}
......@@ -131,17 +127,50 @@ func (obj *object) PSerial() zodb.Tid { return obj.serial }
// pyObject is common base for in-RAM implementations of ZODB Python objects.
type pyObject struct {
object
pyclass pickle.Class
pyclass pickle.Class
// pystate interface{}
// protected by object.mu
instance PyStateful
loaderr error // if there was error at state loading
ready chan struct{} // activation complete
loaded *loaded
// 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) PyState() interface{} { return pyobj.pystate }
// loaded indicates object's load state/result.
//
// when !ready the loading is in progress.
// when ready the loading has been completed.
type loaded struct {
ready chan struct{} // closed when loading finishes
// error from the load.
// if there was no error loaded data goes to object state.
err error
}
// XXX
type Stateful interface {
// DropState should discard in-RAM object state.
DropState()
}
// PyStateful is the interface describing in-RAM object whose data state can be
// exchanged as Python data.
type PyStateful interface {
Stateful
// PySetState should set state of the in-RAM object from Python data.
// Analog of __setstate__() in Python.
PySetState(pystate interface{}) error
// PyGetState should return state of the in-RAM object as Python data.
// Analog of __getstate__() in Python.
//PyGetState() interface{} XXX
}
// Connection represents a view of ZODB database.
//
......@@ -357,7 +386,7 @@ func registerClass(classPath string, classNew func(*pyObject)PyObject) {
// newGhost creates new ghost object corresponding to pyclass and oid.
func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) PyObject {
pyobj := &pyObject{
object: object{jar: conn, oid: oid, serial: 0},
object: object{jar: conn, oid: oid, serial: 0, state: GHOST},
pyclass: pyclass,
}
......@@ -392,22 +421,16 @@ func (d *dummyPyInstance) PySetState(pystate interface{}) error {
// ----------------------------------------
/*
func (obj *object) PActivate(ctx context.Context) error {
// XXX
}
func (obj *object) PDeactivate() {
// XXX
}
// PInvalidate
*/
// object's activate & friends that only manage base activation state, without actually loading data.
// object's pactivate & friends that only manage base activation state, without actually loading data.
// XXX -> incref ?
func (obj *object) pactivate() (load bool) {
// activate increments object reference counter.
//
// it returns whether object data needs to be loaded.
// must be called with .mu held.
func (obj *object) activate() (load bool) {
obj.refcnt++
return (obj.refcnt == 1 && obj.state == GHOST)
/*
nuse := atomic.AddInt32(&obj.refcnt, +1)
if nuse == 1 {
// we become responsible for loading object's data.
......@@ -415,21 +438,28 @@ func (obj *object) pactivate() (load bool) {
return true
}
return false
*/
}
// XXX -> decref ?
func (obj *object) pdeactivate() (drop bool) {
nuse := atomic.AddInt32(&obj.refcnt, -1)
if nuse < 0 {
panic("pdeactivate: nuse < 0")
// deactivate decrements object reference counter.
//
// it returns whether object data needs to be discarded.
// must be called with .mu held.
func (obj *object) deactivate() (drop bool) {
//nuse := atomic.AddInt32(&obj.refcnt, -1)
obj.refcnt--
if obj.refcnt < 0 {
panic("deactivate: refcnt < 0")
}
if nuse > 0 {
if obj.refcnt > 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.
drop = true
if obj.state >= CHANGED {
drop = false
}
// XXX -> pyObject?
if drop {
......@@ -440,7 +470,7 @@ func (obj *object) pdeactivate() (drop bool) {
if drop {
obj.serial = 0
//obj.insance.DropState()
obj.instance.DropState()
}
return drop
......@@ -449,26 +479,35 @@ func (obj *object) pdeactivate() (drop bool) {
// PActivate implements Object.
func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
load := pyobj.object.pactivate()
pyobj.mu.Lock()
doload := pyobj.activate()
defer func() {
if err != nil {
// no need to check for drop - the state is already
// dropped - we just need to decref here.
pyobj.object.pdeactivate()
// XXX locking?
pyobj.deactivate()
}
}()
if !load {
if !doload {
loading := pyobj.loading
pyobj.mu.Unlock()
// someone else is already activated/activating the object.
// wait for its loading to complete and we are done.
select {
case <-ctx.Done():
return ctx.Err() // XXX err ctx
case <-pyobj.ready:
return pyobj.loaderr // XXX err ctx?
return ctx.Err() // XXX err ctx
case <-loading.ready:
return loading.err // XXX err ctx?
}
}
// we become responsible for loading the object
loading := &loading{ready: make(chan struct{})}
pyobj.loading = loading // XXX assert before it was = nil ?
pyobj.mu.Unlock()
pyclass, pystate, serial, err := pyobj.jar.loadpy(ctx, pyobj.oid)
if err == nil && pyclass != pyobj.pyclass {
// complain pyclass changed
......@@ -477,6 +516,8 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
pystate = nil
}
pyobj.mu.Lock()
pyobj.serial = serial
// pyobj.pystate = pystate
......@@ -487,16 +528,17 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
pyobj.instance.DropState()
}
pyobj.loaderr = err
// XXX unlock
close(pyobj.ready)
loading.err = err
close(loading.ready)
return err // XXX err ctx
}
// PDeactivate implements Object.
func (pyobj *pyObject) PDeactivate() {
drop := pyobj.object.pdeactivate()
drop := pyobj.pdeactivate()
if !drop {
return
}
......
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