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

.

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