From d3c8658475a96353ed67be48b301956bb9b06011 Mon Sep 17 00:00:00 2001
From: Kirill Smelkov <kirr@nexedi.com>
Date: Tue, 17 Jul 2018 14:46:52 +0300
Subject: [PATCH] .

---
 wcfs/misc.go |   2 +-
 wcfs/wcfs.go |   3 +-
 wcfs/weak.go |   2 +-
 wcfs/xxx.go  | 153 +++++++++++++++++++++++++++++++++++++++++
 wcfs/zodb.go | 187 ++++++++-------------------------------------------
 5 files changed, 185 insertions(+), 162 deletions(-)
 create mode 100644 wcfs/xxx.go

diff --git a/wcfs/misc.go b/wcfs/misc.go
index d2bfba3..aaea2d9 100644
--- a/wcfs/misc.go
+++ b/wcfs/misc.go
@@ -55,7 +55,7 @@ func (f *StaticFile) Read(_ nodefs.File, dest []byte, off int64, _ *fuse.Context
 
 // mkdir adds child to parent as directory.
 //
-// Note: parent must to be already in the filesystem tree - i.e. associated
+// Note: parent must be already in the filesystem tree - i.e. associated
 // with inode. if not - nodefs will panic in Inode.NewChild on nil dereference.
 func mkdir(parent nodefs.Node, name string, child nodefs.Node) {
 	parent.Inode().NewChild(name, true, child)
diff --git a/wcfs/wcfs.go b/wcfs/wcfs.go
index f14aa9e..0f1b525 100644
--- a/wcfs/wcfs.go
+++ b/wcfs/wcfs.go
@@ -446,6 +446,7 @@ func (br *BigFileRoot) Mkdir(name string, mode uint32, _ *fuse.Context) (*nodefs
 // ZData
 //	str (chunk)
 
+
 // Read implements reading from /bigfile/<bigfileX>/head/data.
 // XXX and from /bigfile/<bigfileX>/@<tidX>/data.
 /*
@@ -464,7 +465,7 @@ func (bf *BigFile) Read(_ nodefs.File, dest []byte, off int64, _ fuse.Context) (
 // For the data itself - we put it to kernel cache and always deactivate from
 // ZODB right after that.
 //
-// XXX set it to Connection.CacheControl
+// TODO set it to Connection.CacheControl
 type zodbCacheControl struct {}
 
 func (cc *zodbCacheControl) WantEvict(obj Object) {
diff --git a/wcfs/weak.go b/wcfs/weak.go
index eb7be92..ce4c0e0 100644
--- a/wcfs/weak.go
+++ b/wcfs/weak.go
@@ -22,7 +22,7 @@
 // See https://www.nexedi.com/licensing for rationale and options.
 
 package main
-// weak reference
+// weak references
 
 import (
 	"runtime"
diff --git a/wcfs/xxx.go b/wcfs/xxx.go
new file mode 100644
index 0000000..17e4610
--- /dev/null
+++ b/wcfs/xxx.go
@@ -0,0 +1,153 @@
+package main
+/*
+// 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 interface{} // XXX -> pyObject iface
+	err   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, load.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)
+	}
+*/
+
+/*
+{
+	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
+		}
+	}
+
+	// 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)
+	}
+}
+*/
+
+/*
+	// XXX -> loadpy
+	buf, serial, err := conn.stor.Load(ctx, zodb.Xid{Oid: oid, At: conn.at})
+	if err != nil {
+		return nil, err
+	}
+
+	pyclass, pystate, err := zodb.PyData(buf.Data).Decode()
+	if err != nil {
+		return nil, err	// XXX err ctx
+	}
+
+	buf.Release()
+
+	return &pyObject{
+		object:  object{jar: conn,oid: oid, serial: serial},
+		pyclass: pyclass,
+		pystate: pystate,
+	}, nil
+}
+*/
+
+/*
+func (conn *Connection) load(ctx context.Context, oid zodb.Oid) (*pyObject, error) {
+	// XXX
+}
+*/
+
diff --git a/wcfs/zodb.go b/wcfs/zodb.go
index e8a54a5..00e8a1d 100644
--- a/wcfs/zodb.go
+++ b/wcfs/zodb.go
@@ -25,17 +25,22 @@ import (
 	pickle "github.com/kisielk/og-rek"
 )
 
+// XXX make methods private, e.g. _pJar ?
+
 // Object is the interface that every in-RAM object representing any ZODB object implements.
 type Object interface {
-	// XXX make methods private, e.g. _pJar ?
-	PJar()    *Connection
-	POid()    zodb.Oid
+	PJar()    *Connection	// Connection this in-RAM object is part of.
+	POid()    zodb.Oid	// object ID in the database.
+
+	// object serial as of database state for particular Connection (PJar).
+	// 0 if not yet loaded (XXX ok?)
 	PSerial() zodb.Tid
 
+
 	// PActivate brings object to live state.
 	//
 	// It requests to persistency layer that in-RAM object data to be present.
-	// If object state is not in RAM - it is loaded from the database.
+	// If object state was not in RAM - it is loaded from the database.
 	//
 	// On successful return the object data is either the same as in the
 	// database or, if this data was previously modified by user of
@@ -53,7 +58,7 @@ type Object interface {
 	//
 	// Note that it is valid to have several concurrent uses of object
 	// data, each protected with corresponding PActivate/PDeactivate pair:
-	// As long as there is still any PActivate not yet compensated with
+	// as long as there is still any PActivate not yet compensated with
 	// corresponding PDeactivate, object data will assuredly stay alive in RAM.
 	//
 	// Besides exotic cases, the caller thus must not use object's data
@@ -66,7 +71,18 @@ type Object interface {
 	// database, or it was modified, that in-RAM data must be forgotten.
 	//
 	// PInvalidate must not be called while there is any in-progress
-	// object's data use (PActivate / PDeactivate).
+	// object's data use (PActivate till PDeactivate).
+	//
+	// In practice this means that:
+	//
+	//	- application must make sure to finish all objects accesses
+	//	  before transaction boundary: at transaction boundary - either
+	//	  at abort or commit, the persistency layer will sync to
+	//	  database and process invalidations.
+	//
+	//	- if PInvalidate is explicitly called by application, the
+	//	  application must care to make sure it does not access the
+	//	  object data simultaneously.
 	PInvalidate()
 }
 
@@ -79,7 +95,7 @@ type PyObject interface {
 
 }
 
-// object is common base for in-RAM representation of ZODB object.
+// object is common base for in-RAM representation of ZODB objects.
 type object struct {
 	jar	*Connection
 	oid	zodb.Oid
@@ -119,7 +135,7 @@ type LiveCacheControl interface {
 //
 // The view is representing state of ZODB objects as of `at` transaction.
 //
-// XXX Connection changes are private and are isolated from changes in other Connections.
+// Connection changes are private and are isolated from changes in other Connections.
 //
 // XXX Connection, and {Py}Object methods that relate to it, are not safe for
 // modifications from multiple goroutines simultaneously.
@@ -178,23 +194,12 @@ type Connection struct {
 	// Hopefully we don't have cycles with ZBtree/ZBucket	XXX verify this
 	objmu  sync.Mutex
 	objtab map[zodb.Oid]*WeakRef	// oid -> WeakRef(PyObject)
-	//objtab map[zodb.Oid]interface{}	// oid -> WeakRef(pyObject) | loadInProgress
 
 
-	cacheControl ConnCacheControl
+	// hooks for application to influence live caching decisions.
+	cacheControl LiveCacheControl
 }
 
-/*
-// 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 interface{} // XXX -> pyObject iface
-	err   error
-}
-*/
 
 // Get returns in-RAM object corresponding to specified ZODB object according to current database view.
 //
@@ -238,60 +243,6 @@ func (conn *Connection) Get(ctx context.Context, oid zodb.Oid) (interface{} /*Py
 	return xobj, nil
 }
 
-/*
-	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, load.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)
-	}
-*/
 
 // wrongClassError is the error cause returned when object's class is not what was expected.
 type wrongClassError struct {
@@ -347,90 +298,6 @@ func (conn *Connection) get(pyclass pickle.Class, oid zodb.Oid) (PyObject, error
 }
 
 
-/*
-{
-	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
-		}
-	}
-
-	// 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)
-	}
-}
-*/
-
-/*
-	// XXX -> loadpy
-	buf, serial, err := conn.stor.Load(ctx, zodb.Xid{Oid: oid, At: conn.at})
-	if err != nil {
-		return nil, err
-	}
-
-	pyclass, pystate, err := zodb.PyData(buf.Data).Decode()
-	if err != nil {
-		return nil, err	// XXX err ctx
-	}
-
-	buf.Release()
-
-	return &pyObject{
-		object:  object{jar: conn,oid: oid, serial: serial},
-		pyclass: pyclass,
-		pystate: pystate,
-	}, nil
-}
-*/
-
-/*
-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 {
@@ -447,6 +314,7 @@ func (conn *Connection) loadpy(ctx context.Context, oid zodb.Oid) (pyclass pickl
 	return pyclass, pystate, serial, nil
 }
 
+
 // path(class) -> new(pyobj)
 var classTab = make(map[string]func(*pyObject)PyObject)
 
@@ -576,5 +444,6 @@ func (pyobj *pyObject) PDeactivate() {
 }
 
 // XXX pyobj.PInvalidate()	= deactivate without checking if state != modified
+// XXX panic if refcnt != 0  (object being used)
 
 
-- 
2.30.9