Commit f532e0f2 authored by Kirill Smelkov's avatar Kirill Smelkov

.

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