From 488550a081769f491f326c7949add6cb9f0cc0b4 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Thu, 28 Nov 2024 23:00:29 -0600 Subject: [PATCH] add example code. add sync.onlyOnce --- cache.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ init.go | 4 ++++ structs.go | 6 ++++++ 3 files changed, 68 insertions(+) create mode 100644 cache.go diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..c139306 --- /dev/null +++ b/cache.go @@ -0,0 +1,58 @@ +// I put this here to look at and think about +// from mod/sumdb/cache.go + +// Parallel cache. +// This file is copied from cmd/go/internal/par. + +package forgepb + +import ( + "sync" + "sync/atomic" +) + +// parCache runs an action once per key and caches the result. +type parCache struct { + m sync.Map +} + +type cacheEntry struct { + done uint32 + mu sync.Mutex + result interface{} +} + +// Do calls the function f if and only if Do is being called for the first time with this key. +// No call to Do with a given key returns until the one call to f returns. +// Do returns the value returned by the one call to f. +func (c *parCache) Do(key interface{}, f func() interface{}) interface{} { + entryIface, ok := c.m.Load(key) + if !ok { + entryIface, _ = c.m.LoadOrStore(key, new(cacheEntry)) + } + e := entryIface.(*cacheEntry) + if atomic.LoadUint32(&e.done) == 0 { + e.mu.Lock() + if atomic.LoadUint32(&e.done) == 0 { + e.result = f() + atomic.StoreUint32(&e.done, 1) + } + e.mu.Unlock() + } + return e.result +} + +// Get returns the cached result associated with key. +// It returns nil if there is no such result. +// If the result for key is being computed, Get does not wait for the computation to finish. +func (c *parCache) Get(key interface{}) interface{} { + entryIface, ok := c.m.Load(key) + if !ok { + return nil + } + e := entryIface.(*cacheEntry) + if atomic.LoadUint32(&e.done) == 0 { + return nil + } + return e.result +} diff --git a/init.go b/init.go index 0f400f8..874439c 100644 --- a/init.go +++ b/init.go @@ -24,6 +24,10 @@ func init() { func Init() *Forge { f := new(Forge) + + // cache.go has Do() + // f.initOnce.Do(f.initWork) + f.Config = new(ForgeConfigs) // load the ~/.config/forge/ config diff --git a/structs.go b/structs.go index 05ed89e..34550b5 100644 --- a/structs.go +++ b/structs.go @@ -1,12 +1,18 @@ package forgepb import ( + sync "sync" + "go.wit.com/lib/protobuf/gitpb" "go.wit.com/lib/protobuf/zoopb" ) // maybe an interface someday? type Forge struct { + // one-time initialized data + initOnce sync.Once + initErr error // init error, if any + goSrc string // the path to go/src Config *ForgeConfigs // config repos for readonly, private, etc Repos *gitpb.Repos