Commit 7be36e9e authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent a19787b9
...@@ -36,7 +36,7 @@ type KEY int64 ...@@ -36,7 +36,7 @@ type KEY int64
// are chained together via 'next', so that the entire BTree contents // are chained together via 'next', so that the entire BTree contents
// can be traversed in sorted order quickly and easily. // can be traversed in sorted order quickly and easily.
type ZBucket struct { type ZBucket struct {
pyobj *PyObject pyobj *pyObject
next *ZBucket // the bucket with the next-larger keys next *ZBucket // the bucket with the next-larger keys
keys []KEY // 'len' keys, in increasing order keys []KEY // 'len' keys, in increasing order
...@@ -54,7 +54,7 @@ type zBTreeItem struct { ...@@ -54,7 +54,7 @@ type zBTreeItem struct {
// See https://github.com/zopefoundation/BTrees/blob/4.5.0-1-gc8bf24e/BTrees/Development.txt#L198 // See https://github.com/zopefoundation/BTrees/blob/4.5.0-1-gc8bf24e/BTrees/Development.txt#L198
// for details. // for details.
type ZBTree struct { type ZBTree struct {
pyobj *PyObject pyobj *pyObject
// firstbucket points to the bucket containing the smallest key in // firstbucket points to the bucket containing the smallest key in
// the BTree. This is found by traversing leftmost child pointers // the BTree. This is found by traversing leftmost child pointers
...@@ -239,11 +239,11 @@ func (b *ZBucket) PActivate(ctx context.Context) error { ...@@ -239,11 +239,11 @@ func (b *ZBucket) PActivate(ctx context.Context) error {
// ---------------------------------------- // ----------------------------------------
func bucketNew(pyobj *PyObject) interface{} { func bucketNew(pyobj *pyObject) interface{} {
return &ZBucket{pyobj: pyobj} return &ZBucket{pyobj: pyobj}
} }
func btreeNew(pyobj *PyObject) interface{} { func btreeNew(pyobj *pyObject) interface{} {
return &ZBTree{pyobj: pyobj} return &ZBTree{pyobj: pyobj}
} }
......
...@@ -39,7 +39,7 @@ import ( ...@@ -39,7 +39,7 @@ import (
// ZBigFile mimics ZBigFile from python. // ZBigFile mimics ZBigFile from python.
type ZBigFile struct { type ZBigFile struct {
pyobj *PyObject pyobj *pyObject
blksize int64 blksize int64
blktab *ZBTree // LOBtree{} blk -> ZBlk*(blkdata) blktab *ZBTree // LOBtree{} blk -> ZBlk*(blkdata)
......
...@@ -16,6 +16,7 @@ package main ...@@ -16,6 +16,7 @@ package main
import ( import (
"context" "context"
"fmt"
"sync" "sync"
"lab.nexedi.com/kirr/neo/go/zodb" "lab.nexedi.com/kirr/neo/go/zodb"
...@@ -23,16 +24,16 @@ import ( ...@@ -23,16 +24,16 @@ import (
pickle "github.com/kisielk/og-rek" pickle "github.com/kisielk/og-rek"
) )
// Object is common base for in-process representation of ZODB object. // object is common base for in-process representation of ZODB object.
type Object struct { type object struct {
jar *Connection jar *Connection
oid zodb.Oid oid zodb.Oid
serial zodb.Tid serial zodb.Tid
} }
// PyObject is common base for in-process representation of ZODB Python objects. // pyObject is common base for in-process representation of ZODB Python objects.
type PyObject struct { type pyObject struct {
Object object
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__()
...@@ -98,7 +99,7 @@ type Connection struct { ...@@ -98,7 +99,7 @@ type Connection struct {
// NOTE2 finalizers don't run on when they are attached to an object in cycle. // NOTE2 finalizers don't run on when they are attached to an object in cycle.
// Hopefully we don't have cycles with ZBtree/ZBucket XXX verify this // Hopefully we don't have cycles with ZBtree/ZBucket XXX verify this
objmu sync.Mutex objmu sync.Mutex
//objtab map[zodb.Oid]interface{} // oid -> WeakRef(PyObject) | loadInProgress //objtab map[zodb.Oid]interface{} // oid -> WeakRef(pyObject) | loadInProgress
objtab map[zodb.Oid]*WeakRef objtab map[zodb.Oid]*WeakRef
} }
...@@ -109,7 +110,7 @@ type loadInProgress struct { ...@@ -109,7 +110,7 @@ type loadInProgress struct {
ready chan struct{} // closed when loading finishes ready chan struct{} // closed when loading finishes
// result of the load // result of the load
pyobj interface{} // XXX -> PyObject iface pyobj interface{} // XXX -> pyObject iface
err error err error
} }
*/ */
...@@ -143,7 +144,10 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py ...@@ -143,7 +144,10 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
return nil, err // XXX errctx return nil, err // XXX errctx
} }
xobj = conn.get(pyclass, oid) xobj, err = conn.get(pyclass, oid)
if err != nil {
return nil, err
}
// XXX we are dropping just loaded pystate. Usually Get should be used // XXX we are dropping just loaded pystate. Usually Get should be used
// to only load root object, so maybe that is ok. // to only load root object, so maybe that is ok.
...@@ -209,6 +213,15 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py ...@@ -209,6 +213,15 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
*/ */
} }
// wrongClassError is the error cause returned when object's class is wrong.
type wrongClassError struct {
want, have pickle.Class
}
func (e *wrongClassError) Error() string {
return fmt.Sprintf("wrong class: want %q; have %q", e.want, e.have)
}
// get returns in-RAM object corresponding to specified ZODB object according to current database 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. // If there is already in-RAM object that corresponds to oid, that in-RAM object is returned.
...@@ -219,20 +232,34 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py ...@@ -219,20 +232,34 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
// //
// use-case: in ZODB references are (pyclass, oid), so new ghost is created // use-case: in ZODB references are (pyclass, oid), so new ghost is created
// without further loading anything. // without further loading anything.
func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) interface{}/*PyObject*/ { func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) (interface{}/*PyObject*/, error) {
conn.objmu.Lock() // XXX -> rlock conn.objmu.Lock() // XXX -> rlock
wobj := conn.objtab[oid] wobj := conn.objtab[oid]
var xobj interface{} var xobj interface{}
checkClass := false
if wobj != nil { if wobj != nil {
xobj = wobj.Get() xobj = wobj.Get()
// XXX check pyclass match.
} }
if xobj == nil { if xobj == nil {
xobj = conn.newGhost(pyclass, oid) xobj = conn.newGhost(pyclass, oid)
conn.objtab[oid] = NewWeakRef(xobj) conn.objtab[oid] = NewWeakRef(xobj)
} else {
checkClass = true
} }
conn.objmu.Unlock() conn.objmu.Unlock()
return xobj
if checkClass {
if cls := xobj.PyClass(); pyclass != cls {
return nil, &zodb.OpError{
URL: conn.stor.URL(),
Op: fmt.Sprintf("@%s: get", conn.at), // XXX abuse
Args: oid,
Err: &wrongClassError{pyclass, cls},
}
}
}
return xobj, nil
} }
...@@ -306,8 +333,8 @@ func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) interface{}/*PyO ...@@ -306,8 +333,8 @@ func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) interface{}/*PyO
buf.Release() buf.Release()
return &PyObject{ return &pyObject{
Object: Object{jar: conn,oid: oid, serial: serial}, object: object{jar: conn,oid: oid, serial: serial},
pyclass: pyclass, pyclass: pyclass,
pystate: pystate, pystate: pystate,
}, nil }, nil
...@@ -315,7 +342,7 @@ func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) interface{}/*PyO ...@@ -315,7 +342,7 @@ func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) interface{}/*PyO
*/ */
/* /*
func (conn *Connection) load(ctx context.Context, oid zodb.Oid) (*PyObject, error) { func (conn *Connection) load(ctx context.Context, oid zodb.Oid) (*pyObject, error) {
// XXX // XXX
} }
*/ */
...@@ -337,20 +364,20 @@ func (conn *Connection) loadpy(ctx context.Context, oid zodb.Oid) (pyclass pickl ...@@ -337,20 +364,20 @@ func (conn *Connection) loadpy(ctx context.Context, oid zodb.Oid) (pyclass pickl
} }
// path(class) -> new(pyobj) // path(class) -> new(pyobj)
var classTab = make(map[string]func(*PyObject)interface{}) var classTab = make(map[string]func(*pyObject)interface{})
// registerClass registers python class to be transformed to Go instance // registerClass registers python class to be transformed to Go instance
// created via classNew. // created via classNew.
// //
// must be called from global init(). // must be called from global init().
func registerClass(classPath string, classNew func(*PyObject)interface{}) { func registerClass(classPath string, classNew func(*pyObject)interface{}) {
classTab[classPath] = classNew classTab[classPath] = classNew
} }
// 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) interface{} { func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) interface{} {
pyobj := &PyObject{ pyobj := &pyObject{
Object: Object{jar: conn, oid: oid, serial: 0}, object: object{jar: conn, oid: oid, serial: 0},
pyclass: pyclass, pyclass: pyclass,
pystate: nil, pystate: nil,
} }
...@@ -370,7 +397,7 @@ func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) interface{} ...@@ -370,7 +397,7 @@ func (conn *Connection) newGhost(pyclass pickle.Class, oid zodb.Oid) interface{}
// PDeactivates transforms object to ghost state. // PDeactivates transforms object to ghost state.
// //
// In ghost state object data is dropped and only oid/pyclass information is left in RAM. // In ghost state object data is dropped and only oid/pyclass information is left in RAM.
func (pyobj *PyObject) PDeactivate() { func (pyobj *pyObject) PDeactivate() {
// FIXME if state=modified PDeactivate must be noop. // FIXME if state=modified PDeactivate must be noop.
pyobj.pystate = nil pyobj.pystate = nil
...@@ -380,7 +407,7 @@ func (pyobj *PyObject) PDeactivate() { ...@@ -380,7 +407,7 @@ func (pyobj *PyObject) PDeactivate() {
// PActivate brings object to live state. // PActivate brings object to live state.
// //
// If object state is not in RAM - it is loaded from the database. // If object state is not in RAM - it is loaded from the database.
func (pyobj *PyObject) PActivate(ctx context.Context) error { func (pyobj *pyObject) PActivate(ctx context.Context) error {
if pyobj.pystate != nil { if pyobj.pystate != nil {
return nil // already loaded return nil // already loaded
} }
......
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