Commit 0f146bcb authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent f532e0f2
......@@ -18,7 +18,7 @@ import (
"context"
"fmt"
"sync"
"sync/atomic"
// "sync/atomic"
"lab.nexedi.com/kirr/neo/go/zodb"
......@@ -109,7 +109,7 @@ const (
// no STICKY - we pin objects in RAM with PActivate
)
// object is common base for in-RAM implementations of ZODB objects.
// object is common base implementation for in-RAM representation of ZODB objects.
type object struct {
jar *Connection
oid zodb.Oid
......@@ -124,30 +124,28 @@ func (obj *object) PJar() *Connection { return obj.jar }
func (obj *object) POid() zodb.Oid { return obj.oid }
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 implementation for in-RAM representation of ZODB Python objects.
type pyObject struct {
object
pyclass pickle.Class
// protected by object.mu
instance PyStateful
loaded *loaded
// loaderr error // if there was error at state loading
// ready chan struct{} // activation complete
loading *loadState
}
func (pyobj *pyObject) PyClass() pickle.Class { return pyobj.pyclass }
//func (pyobj *pyObject) PyState() interface{} { return pyobj.pystate }
// loaded indicates object's load state/result.
// loadState indicates object's load state/result.
//
// when !ready the loading is in progress.
// when ready the loading has been completed.
type loaded struct {
type loadState struct {
ready chan struct{} // closed when loading finishes
// error from the load.
// if there was no error loaded data goes to object state.
// if there was no error, loaded data goes to object state.
err error
}
......@@ -426,6 +424,8 @@ func (d *dummyPyInstance) PySetState(pystate interface{}) error {
// activate increments object reference counter.
//
// it returns whether object data needs to be loaded.
// the caller must adjust .state after loading is complete.
//
// must be called with .mu held.
func (obj *object) activate() (load bool) {
obj.refcnt++
......@@ -452,28 +452,40 @@ func (obj *object) deactivate() (drop bool) {
panic("deactivate: refcnt < 0")
}
if obj.refcnt > 0 {
return // users still left
return false // users still left
}
// no users left. Let's see whether we should transition this object to ghost.
drop = true
if obj.state >= CHANGED {
drop = false
return false
}
// XXX -> pyObject?
if drop {
if cc := obj.jar.cacheControl; cc != nil {
drop = cc.WantEvict(obj.instance)
if cc := obj.jar.cacheControl; cc != nil {
if !cc.WantEvict(obj.instance) {
return false
}
}
if drop {
obj.serial = 0
obj.instance.DropState()
obj.serial = 0
obj.instance.DropState()
obj.state = GHOST
return true
}
// invalidate XXX
//
// must be called with .mu held.
func (obj *object) invalidate() {
if obj.refcnt != 0 {
// object is currently in use
panic("invalidate: refcnt != 0")
}
return drop
obj.serial = 0
obj.instance.DropState()
obj.state = GHOST
}
......@@ -485,16 +497,17 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
if err != nil {
// no need to check for drop - the state is already
// dropped - we just need to decref here.
// XXX locking?
pyobj.deactivate()
//
// XXX locking
pyobj.deactivate() // XXX -> drop?
}
}()
if !doload {
// someone else is already activated/activating the object.
// wait for its loading to complete and we are done.
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
......@@ -504,10 +517,11 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
}
// we become responsible for loading the object
loading := &loading{ready: make(chan struct{})}
loading := &loadState{ready: make(chan struct{})}
pyobj.loading = loading // XXX assert before it was = nil ?
pyobj.mu.Unlock()
// do the loading outside of pyobj lock
pyclass, pystate, serial, err := pyobj.jar.loadpy(ctx, pyobj.oid)
if err == nil && pyclass != pyobj.pyclass {
// complain pyclass changed
......@@ -516,8 +530,12 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
pystate = nil
}
// relock the object
pyobj.mu.Lock()
// XXX assert pyobj.loading == loading
// XXX assert pyobj.state == GHOST
pyobj.serial = serial
// pyobj.pystate = pystate
......@@ -525,36 +543,36 @@ func (pyobj *pyObject) PActivate(ctx context.Context) (err error) {
err = pyobj.instance.PySetState(pystate) // XXX err ctx
}
if err != nil {
pyobj.instance.DropState()
pyobj.instance.DropState() // XXX already in deactivate
} else {
pyobj.state = UPTODATE
}
// XXX unlock
loading.err = err
pyobj.mu.Unlock()
close(loading.ready)
return err // XXX err ctx
}
// PDeactivate implements Object.
func (pyobj *pyObject) PDeactivate() {
drop := pyobj.pdeactivate()
if !drop {
return
}
pyobj.mu.Lock()
defer pyobj.mu.Unlock()
// we have to drop pyobject state
// XXX locking?
// pyobj.pystate = nil
pyobj.instance.DropState()
pyobj.loaderr = nil
pyobj.ready = make(chan struct{})
drop := pyobj.deactivate()
if drop {
pyobj.loading = nil
}
}
// XXX pyobj.PInvalidate() = deactivate without checking if state != modified
// PInvalidate() implements Object.
func (pyobj *pyObject) PInvalidate() {
// XXX panic if refcnt != 0 (object being used)
pyobj.instance.DropState()
pyobj.loaderr = nil
pyobj.ready = make(chan struct{})
pyobj.mu.Lock()
defer pyobj.mu.Unlock()
pyobj.invalidate()
pyobj.loading = nil
}
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