From 28eacdb2bb504a9a845e5a509b58115ff40035d9 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Mon, 22 Jan 2024 14:12:46 -0500 Subject: [PATCH] lang: funcs: ref: Move reference counting code to a new package This also makes it public, although it is designed for internal use only. --- lang/funcs/dage/dage.go | 5 ++-- lang/funcs/dage/txn.go | 5 ++-- lang/funcs/dage/txn_test.go | 4 +-- lang/funcs/{dage => ref}/ref.go | 48 ++++++++++++++++----------------- 4 files changed, 32 insertions(+), 30 deletions(-) rename lang/funcs/{dage => ref}/ref.go (85%) diff --git a/lang/funcs/dage/dage.go b/lang/funcs/dage/dage.go index 8aa2233a..88fff223 100644 --- a/lang/funcs/dage/dage.go +++ b/lang/funcs/dage/dage.go @@ -30,6 +30,7 @@ import ( "github.com/purpleidea/mgmt/engine" "github.com/purpleidea/mgmt/engine/local" + "github.com/purpleidea/mgmt/lang/funcs/ref" "github.com/purpleidea/mgmt/lang/funcs/structs" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" @@ -71,7 +72,7 @@ type Engine struct { // refCount keeps track of vertex and edge references across the entire // graph. - refCount *RefCount + refCount *ref.Count // wgTxn blocks shutdown until the initial Txn has Reversed. wgTxn *sync.WaitGroup @@ -161,7 +162,7 @@ func (obj *Engine) Setup() error { obj.graphMutex = &sync.Mutex{} // TODO: &sync.RWMutex{} ? obj.tableMutex = &sync.RWMutex{} - obj.refCount = (&RefCount{}).Init() + obj.refCount = (&ref.Count{}).Init() obj.wgTxn = &sync.WaitGroup{} diff --git a/lang/funcs/dage/txn.go b/lang/funcs/dage/txn.go index 32c0e5b7..2a5efa31 100644 --- a/lang/funcs/dage/txn.go +++ b/lang/funcs/dage/txn.go @@ -24,6 +24,7 @@ import ( "sort" "sync" + "github.com/purpleidea/mgmt/lang/funcs/ref" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/pgraph" ) @@ -41,7 +42,7 @@ const GraphvizDebug = false // also allows us to change API slightly without re-writing code. type opapi struct { GraphAPI interfaces.GraphAPI - RefCount *RefCount + RefCount *ref.Count } // opfn is an interface that holds the normal op, and the reverse op if we need @@ -272,7 +273,7 @@ type graphTxn struct { // RefCount keeps track of vertex and edge references across the entire // graph. - RefCount *RefCount + RefCount *ref.Count // FreeFunc is a function that will get called by a well-behaved user // when we're done with this Txn. diff --git a/lang/funcs/dage/txn_test.go b/lang/funcs/dage/txn_test.go index e406c177..a3b9278b 100644 --- a/lang/funcs/dage/txn_test.go +++ b/lang/funcs/dage/txn_test.go @@ -142,7 +142,7 @@ func TestTxn1(t *testing.T) { GraphAPI: testGraphAPI, Lock: mutex.Lock, Unlock: mutex.Unlock, - RefCount: (&RefCount{}).Init(), + RefCount: (&ref.Count{}).Init(), } txn := graphTxn.init() @@ -485,7 +485,7 @@ func TestTxnTable(t *testing.T) { GraphAPI: testGraphAPI, Lock: mutex.Lock, Unlock: mutex.Unlock, - RefCount: (&RefCount{}).Init(), + RefCount: (&ref.Count{}).Init(), } txn := graphTxn.init() diff --git a/lang/funcs/dage/ref.go b/lang/funcs/ref/ref.go similarity index 85% rename from lang/funcs/dage/ref.go rename to lang/funcs/ref/ref.go index 74cd80d2..61b51992 100644 --- a/lang/funcs/dage/ref.go +++ b/lang/funcs/ref/ref.go @@ -15,9 +15,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -// Package dage implements a DAG function engine. -// TODO: can we rename this to something more interesting? -package dage +// Package ref implements reference counting for the graph API and function +// engine. +package ref import ( "fmt" @@ -27,9 +27,9 @@ import ( "github.com/purpleidea/mgmt/util/errwrap" ) -// RefCount keeps track of vertex and edge references across the entire graph. -// Make sure to lock access somehow, ideally with the provided Locker interface. -type RefCount struct { +// Count keeps track of vertex and edge references across the entire graph. Make +// sure to lock access somehow, ideally with the provided Locker interface. +type Count struct { // mutex locks this database for read or write. mutex *sync.Mutex @@ -37,18 +37,18 @@ type RefCount struct { vertices map[interfaces.Func]int64 // edges is a reference count of the number of edges used. - edges map[*RefCountEdge]int64 // TODO: hash *RefCountEdge as a key instead + edges map[*CountEdge]int64 // TODO: hash *CountEdge as a key instead } -// RefCountEdge is a virtual "hash" entry for the RefCount edges map key. -type RefCountEdge struct { +// CountEdge is a virtual "hash" entry for the Count edges map key. +type CountEdge struct { f1 interfaces.Func f2 interfaces.Func arg string } // String prints a representation of the references held. -func (obj *RefCount) String() string { +func (obj *Count) String() string { s := "" s += fmt.Sprintf("vertices (%d):\n", len(obj.vertices)) for vertex, count := range obj.vertices { @@ -62,18 +62,18 @@ func (obj *RefCount) String() string { } // Init must be called to initialized the struct before first use. -func (obj *RefCount) Init() *RefCount { +func (obj *Count) Init() *Count { obj.mutex = &sync.Mutex{} obj.vertices = make(map[interfaces.Func]int64) - obj.edges = make(map[*RefCountEdge]int64) + obj.edges = make(map[*CountEdge]int64) return obj // return self so it can be called in a chain } // Lock the mutex that should be used when reading or writing from this. -func (obj *RefCount) Lock() { obj.mutex.Lock() } +func (obj *Count) Lock() { obj.mutex.Lock() } // Unlock the mutex that should be used when reading or writing from this. -func (obj *RefCount) Unlock() { obj.mutex.Unlock() } +func (obj *Count) Unlock() { obj.mutex.Unlock() } // VertexInc increments the reference count for the input vertex. It returns // true if the reference count for this vertex was previously undefined or zero. @@ -81,7 +81,7 @@ func (obj *RefCount) Unlock() { obj.mutex.Unlock() } // to increment a vertex which already has a less than zero count, then this // will panic. This situation is likely impossible unless someone modified the // reference counting struct directly. -func (obj *RefCount) VertexInc(f interfaces.Func) bool { +func (obj *Count) VertexInc(f interfaces.Func) bool { count, _ := obj.vertices[f] obj.vertices[f] = count + 1 if count == -1 { // unlikely, but catch any bugs @@ -94,7 +94,7 @@ func (obj *RefCount) VertexInc(f interfaces.Func) bool { // true if the reference count for this vertex is now zero. True usually means // we'd want to actually remove this vertex now. If you attempt to decrement a // vertex which already has a zero count, then this will panic. -func (obj *RefCount) VertexDec(f interfaces.Func) bool { +func (obj *Count) VertexDec(f interfaces.Func) bool { count, _ := obj.vertices[f] obj.vertices[f] = count - 1 if count == 0 { @@ -107,7 +107,7 @@ func (obj *RefCount) VertexDec(f interfaces.Func) bool { // reference for each arg name in the edge. Since this also increments the // references for the two input vertices, it returns the corresponding two // boolean values for these calls. (This function makes two calls to VertexInc.) -func (obj *RefCount) EdgeInc(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (bool, bool) { +func (obj *Count) EdgeInc(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (bool, bool) { for _, arg := range fe.Args { // ref count each arg r := obj.makeEdge(f1, f2, arg) count := obj.edges[r] @@ -124,7 +124,7 @@ func (obj *RefCount) EdgeInc(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (b // reference for each arg name in the edge. Since this also decrements the // references for the two input vertices, it returns the corresponding two // boolean values for these calls. (This function makes two calls to VertexDec.) -func (obj *RefCount) EdgeDec(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (bool, bool) { +func (obj *Count) EdgeDec(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (bool, bool) { for _, arg := range fe.Args { // ref count each arg r := obj.makeEdge(f1, f2, arg) count := obj.edges[r] @@ -138,7 +138,7 @@ func (obj *RefCount) EdgeDec(f1, f2 interfaces.Func, fe *interfaces.FuncEdge) (b } // FreeVertex removes exactly one entry from the Vertices list or it errors. -func (obj *RefCount) FreeVertex(f interfaces.Func) error { +func (obj *Count) FreeVertex(f interfaces.Func) error { if count, exists := obj.vertices[f]; !exists || count != 0 { return fmt.Errorf("no vertex of count zero found") } @@ -147,8 +147,8 @@ func (obj *RefCount) FreeVertex(f interfaces.Func) error { } // FreeEdge removes exactly one entry from the Edges list or it errors. -func (obj *RefCount) FreeEdge(f1, f2 interfaces.Func, arg string) error { - found := []*RefCountEdge{} +func (obj *Count) FreeEdge(f1, f2 interfaces.Func, arg string) error { + found := []*CountEdge{} for k, count := range obj.edges { //if k == nil { // programming error // continue @@ -169,7 +169,7 @@ func (obj *RefCount) FreeEdge(f1, f2 interfaces.Func, arg string) error { // GC runs the garbage collector on any zeroed references. Note the distinction // between count == 0 (please delete now) and absent from the map. -func (obj *RefCount) GC(graphAPI interfaces.GraphAPI) error { +func (obj *Count) GC(graphAPI interfaces.GraphAPI) error { // debug //fmt.Printf("start refs\n%s", obj.String()) //defer func() { fmt.Printf("end refs\n%s", obj.String()) }() @@ -265,7 +265,7 @@ func (obj *RefCount) GC(graphAPI interfaces.GraphAPI) error { // makeEdge looks up an edge with the "hash" input we are seeking. If it doesn't // find a match, it returns a new one with those fields. -func (obj *RefCount) makeEdge(f1, f2 interfaces.Func, arg string) *RefCountEdge { +func (obj *Count) makeEdge(f1, f2 interfaces.Func, arg string) *CountEdge { for k := range obj.edges { //if k == nil { // programming error // continue @@ -274,7 +274,7 @@ func (obj *RefCount) makeEdge(f1, f2 interfaces.Func, arg string) *RefCountEdge return k } } - return &RefCountEdge{ // not found, so make a new one! + return &CountEdge{ // not found, so make a new one! f1: f1, f2: f2, arg: arg,