Commit 3ec95a81 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent e083af5b
......@@ -235,3 +235,19 @@ func (b *ZBucket) PActivate(ctx context.Context) error {
return nil
}
// ----------------------------------------
func bucketNew(pyobj *PyObject) interface{} {
return &ZBucket{pyobj: pyobj}
}
func btreeNew(pyobj *PyObject) interface{} {
return &ZBtree{pyobj: pyobj}
}
func init() {
registerClass("zodb.BTree.LOBucket", bucketNew)
registerClass("zodb.BTree.LOBtree", btreeNew)
}
......@@ -39,6 +39,8 @@ type PyObject struct {
// Connection represents a view of ZODB database.
//
// The view is representing state of ZODB objects as of `at` transaction.
//
// XXX Connection, and {Py}Object methods that relate to it, are not safe for
// modifications from multiple goroutines simultaneously.
//
......@@ -98,22 +100,26 @@ type Connection struct {
objtab map[zodb.Oid]interface{} // oid -> WeakRef(PyObject) | loadInProgress
}
// loadInProgress entry in Conn.objtab tells users that try to get the entry
// loadInProgress entry in Conn.objtab tells users, that try to get the entry,
// that another goroutine is already in progress of loading it.
type loadInProgress struct {
ready chan struct{} // closed when loading finishes
// result of the load
pyobj *PyObject
pyobj interface{} // XXX -> PyObject iface
err error
}
// Gets loads and decodes object from the database according to its current view.
// Get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created and filled with object's class loaded from the database.
//
// FIXME multiple calls to Get(oid) have to return the same instance.
// XXX this is needed if there are several persistent references to the same object.
// however wendelin.core does not do this.
// The object's data is not neccessarily loaded after Get returns. Use
// PActivate to make sure the object ifs fully loaded.
func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (*PyObject, error) {
// XXX = load raw oid, get its class -> get(pyclass, oid)
conn.objmu.Lock() // XXX -> rlock
objentry := conn.objtab[oid]
......@@ -124,8 +130,73 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (*PyObject, error
select {
case <-ctx.Done():
return nil, ctx.Err() // XXX err ctx
case <-xobj.ready:
return xobj.pyobj, err
case <-load.ready:
// XXX check pyclass match
return load.pyobj, err
}
}
// noone else is loading the object. Investigate whether it is simply not
// in objtab, or it is lost weakref, and if so we have to initiate the load.
var xobj interface{}
if objentry != nil {
wobj := objentry.(*WeakRef)
xobj = wobj.Get()
}
if xpyobj != nil {
// the object is already there
conn.objmu.Unlock()
return xpyobj, nil // XXX + activate?
}
// the object is not there, we have to (re)load it.
load := &loadInProgress{ready: make(chan struct)}
conn.objTab[oid] = load
conn.objmu.Unlock()
pyobj, err = conn.loadpy(ctx, oid)
load.pyobj = pyobj
load.err = err
close(load.ready)
// replace objtab entry with WeakRef
conn.objmu.Lock()
defer conn.objmu.Unlock()
if x := conn.objtab[oid]; x != load {
panic(fmt.Sprintf("zodb.Conn: objtab[%s]: load finished, but it is: %#v", oid, x)
}
if err != nil {
// it was loading error - remove the entry not to cause uncontrolled objtab growth.
// if this object is accessed next time - the load will be retried.
delete(conn.objtab[oid])
} else {
conn.objtab[oid] = NewWeakRef(pyobj)
}
}
// get returns in-RAM object corresponding to specified ZODB object according to current database view.
//
// If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
// Otherwise new in-RAM object is created and filled with object's class loaded from the database.
//
// The object's data is not neccessarily loaded after get returns. Use
// PActivate to make sure the object ifs fully loaded.
//
// use-case: in ZODB references are (pyclass, oid), so new ghost is created without loading anything.
func (conn *Connection) get(ctx context.Context, pyclass pickle.Class, oid zodb.Oid) (*PyObject, error) {
conn.objmu.Lock() // XXX -> rlock
objentry := conn.objtab[oid]
// someone else is already loading the object.
// we have to wait for that someone to finish the load.
if load, ok := objentry.(*loadInProgress); ok {
conn.objmu.Unlock()
select {
case <-ctx.Done():
return nil, ctx.Err() // XXX err ctx
case <-load.ready:
// XXX check pyclass match
return load.pyobj, err
}
}
......@@ -189,6 +260,10 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (*PyObject, error
*/
}
func (conn *Connection) load(ctx context.Context, oid zodb.Oid) (*PyObject, error) {
// XXX
}
func (conn *Connection) loadpy(ctx context.Context, oid zodb.Oid) (pyclass pickle.Class, pystate interface{}, serial zodb.Tid, _ error) {
buf, serial, err := conn.stor.Load(ctx, zodb.Xid{Oid: oid, At: conn.at})
if err != nil {
......@@ -205,16 +280,32 @@ func (conn *Connection) loadpy(ctx context.Context, oid zodb.Oid) (pyclass pickl
return pyclass, pystate, serial, nil
}
// newGhost creates new ghost object.
// path(class) -> new(pyobj)
var classTab = map[string]func(*PyObject)interface{}
// registerClass registers python class to be transformed to Go instance
// created via classNew.
//
// must be called from global init().
func registerClass(pyClassPath string, classNew func(*PyObject)interface{}) {
classTab[classPath] = classNew
}
// newGhost creates new ghost object corresponding to pyclass and oid.
func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) interface{} {
pyobj := PyObject{
pyobj := &PyObject{
Object: Object{jar: conn, oid: oid, serial: 0},
pyclass: pyclass,
pystate: nil,
}
// TODO switch on pyclass and transform e.g. "zodb.BTree.Bucket" -> *ZBucket
return &pyobj
// switch on pyclass and transform e.g. "zodb.BTree.Bucket" -> *ZBucket
classNew := classTab[pyclass.Module + "." + pyclass.Name]
if classNew == nil {
return pyobj // XXX or return error here?
}
return classNew(pyobj)
}
......
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