diff --git a/go.mod b/go.mod index 63e62eba..a50e45b6 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fxamacker/cbor/v2 v2.4.0 github.com/segmentio/ksuid v1.0.4 github.com/spf13/cobra v1.3.0 - github.com/tliron/kutil v0.1.51 + github.com/tliron/kutil v0.1.52 github.com/tliron/yamlkeys v1.3.5 ) diff --git a/go.sum b/go.sum index 582643f8..58b5ea49 100644 --- a/go.sum +++ b/go.sum @@ -875,8 +875,8 @@ github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tliron/kutil v0.1.51 h1:YAao8Gcl6URZtinsnQYJ122TC2V0dKbqc4ZirsDMrCM= -github.com/tliron/kutil v0.1.51/go.mod h1:5Q84smrZi52Ansuw8PfwG3JJ+ysqgxNzuQbcWkslMYs= +github.com/tliron/kutil v0.1.52 h1:h4MQSaoutWYYj5/T/K/GOYEKeLsoo8D3vlTW0f6NUtk= +github.com/tliron/kutil v0.1.52/go.mod h1:MQI/OQWJSUSaSbJjFAl7zlt7Q1nrl6sxuMtIiRxECz0= github.com/tliron/yamlkeys v1.3.5 h1:zfNwrtxNP9pyj2UJV8SgLj6jUP4bsHwFtBByZyIg4AM= github.com/tliron/yamlkeys v1.3.5/go.mod h1:8kJ1A/1s3p/I3MQUAbtv72dPEyQGoh0ZkQp0UAkABBo= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/puccini-tosca/commands/parse.go b/puccini-tosca/commands/parse.go index 2a0a5433..91b7d8fa 100644 --- a/puccini-tosca/commands/parse.go +++ b/puccini-tosca/commands/parse.go @@ -139,16 +139,18 @@ func Parse(url string) (*parser.ServiceContext, *normal.ServiceTemplate) { // Phase 4: Inheritance if stopAtPhase >= 4 { - tasks := serviceContext.GetInheritTasks() if ToPrintPhase(4) { - if len(dumpPhases) > 1 { - terminal.Printf("%s\n", terminal.Stylize.Heading("Inheritance Tasks")) - tasks.Print(1) - } else { - tasks.Print(0) - } + serviceContext.Inherit(func(tasks parser.Tasks) { + if len(dumpPhases) > 1 { + terminal.Printf("%s\n", terminal.Stylize.Heading("Inheritance Tasks")) + tasks.Print(1) + } else { + tasks.Print(0) + } + }) + } else { + serviceContext.Inherit(nil) } - tasks.Drain() } if serviceContext.Root == nil { diff --git a/puccini_test.go b/puccini_test.go index 54d943bb..13a5bdf4 100644 --- a/puccini_test.go +++ b/puccini_test.go @@ -75,7 +75,7 @@ type Context struct { func NewContext(t *testing.T) *Context { var root string var ok bool - if root, ok = os.LookupEnv("ROOT"); !ok { + if root, ok = os.LookupEnv("PUCCINI_TEST_ROOT"); !ok { root, _ = os.Getwd() } diff --git a/scripts/test b/scripts/test index 76243939..bc79e43e 100755 --- a/scripts/test +++ b/scripts/test @@ -11,6 +11,7 @@ HERE=$(dirname "$(readlink --canonicalize "$BASH_SOURCE")") m 'testing...' -ROOT=$ROOT \ +PUCCINI_TEST_ROOT=$ROOT \ +KUTIL_LOCK_DEFAULT=debug \ GORACE=history_size=7 \ go test -count=1 -timeout=30s "$MODULE" "$@" diff --git a/tosca/entity.go b/tosca/entity.go index 69222b33..2b86437c 100644 --- a/tosca/entity.go +++ b/tosca/entity.go @@ -53,30 +53,6 @@ func (self EntityPtrSet) Contains(entityPtr EntityPtr) bool { return ok } -// -// EntityWork -// - -type EntityWork = EntityPtrSet - -func (self EntityWork) Start(entityPtr EntityPtr) bool { - if self.Contains(entityPtr) { - return false - } else { - self.Add(entityPtr) - return true - } -} - -func (self EntityWork) TraverseEntities(entityPtr EntityPtr, traverse reflection.EntityTraverser) { - reflection.TraverseEntities(entityPtr, false, func(entityPtr EntityPtr) bool { - if self.Start(entityPtr) { - return traverse(entityPtr) - } - return false - }) -} - // // PreReadable // diff --git a/tosca/hierarchy.go b/tosca/hierarchy.go index 9006f90a..ccdc6e24 100644 --- a/tosca/hierarchy.go +++ b/tosca/hierarchy.go @@ -27,7 +27,7 @@ func NewHierarchy() *Hierarchy { return new(Hierarchy) } -func NewHierarchyFor(entityPtr EntityPtr, work EntityWork, hierarchyContext HierarchyContext) *Hierarchy { +func NewHierarchyFor(entityPtr EntityPtr, work reflection.EntityWork, hierarchyContext HierarchyContext) *Hierarchy { self := NewHierarchy() work.TraverseEntities(entityPtr, func(entityPtr EntityPtr) bool { diff --git a/tosca/normal/service-template.go b/tosca/normal/service-template.go index 616e9211..147e5a4d 100644 --- a/tosca/normal/service-template.go +++ b/tosca/normal/service-template.go @@ -35,31 +35,32 @@ func NewServiceTemplate() *ServiceTemplate { } } +// +// Normalizable +// + +type Normalizable interface { + NormalizeServiceTemplate() *ServiceTemplate +} + // From Normalizable interface func NormalizeServiceTemplate(entityPtr tosca.EntityPtr) (*ServiceTemplate, bool) { - var s *ServiceTemplate + var serviceTemplate *ServiceTemplate reflection.TraverseEntities(entityPtr, false, func(entityPtr tosca.EntityPtr) bool { if normalizable, ok := entityPtr.(Normalizable); ok { - s = normalizable.NormalizeServiceTemplate() + serviceTemplate = normalizable.NormalizeServiceTemplate() // Only one entity should implement the interface return false + } else { + return true } - return true }) - if s == nil { + if serviceTemplate != nil { + return serviceTemplate, true + } else { return nil, false } - - return s, true -} - -// -// Normalizable -// - -type Normalizable interface { - NormalizeServiceTemplate() *ServiceTemplate } diff --git a/tosca/parser/context.go b/tosca/parser/context.go index 3d1211e8..9e7bdd9a 100644 --- a/tosca/parser/context.go +++ b/tosca/parser/context.go @@ -4,6 +4,7 @@ import ( "sync" "github.com/tliron/kutil/problems" + "github.com/tliron/kutil/reflection" "github.com/tliron/kutil/terminal" "github.com/tliron/kutil/util" "github.com/tliron/puccini/tosca" @@ -16,20 +17,20 @@ import ( type Context struct { readCache sync.Map // entityPtr or Promise - lookupFieldsWork tosca.EntityWork - addHierarchyWork tosca.EntityWork - getInheritTaskWork tosca.EntityWork - renderWork tosca.EntityWork - entitiesLock util.RWLocker + lookupFieldsWork reflection.EntityWork + addHierarchyWork reflection.EntityWork + getInheritTaskWork reflection.EntityWork + renderWork reflection.EntityWork + lock util.RWLocker } func NewContext() *Context { return &Context{ - lookupFieldsWork: make(tosca.EntityWork), - addHierarchyWork: make(tosca.EntityWork), - getInheritTaskWork: make(tosca.EntityWork), - renderWork: make(tosca.EntityWork), - entitiesLock: util.NewDefaultRWLocker(), + lookupFieldsWork: make(reflection.EntityWork), + addHierarchyWork: make(reflection.EntityWork), + getInheritTaskWork: make(reflection.EntityWork), + renderWork: make(reflection.EntityWork), + lock: util.NewDefaultRWLocker(), } } diff --git a/tosca/parser/inputs.go b/tosca/parser/inputs.go index d59ef04c..03325288 100644 --- a/tosca/parser/inputs.go +++ b/tosca/parser/inputs.go @@ -6,8 +6,8 @@ import ( ) func (self *ServiceContext) SetInputs(inputs map[string]ard.Value) { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() tosca.SetInputs(self.Root.EntityPtr, inputs) } diff --git a/tosca/parser/normalize.go b/tosca/parser/normalize.go index 8a2b46d4..97531721 100644 --- a/tosca/parser/normalize.go +++ b/tosca/parser/normalize.go @@ -5,8 +5,8 @@ import ( ) func (self *ServiceContext) Normalize() (*normal.ServiceTemplate, bool) { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() return normal.NormalizeServiceTemplate(self.Root.EntityPtr) } diff --git a/tosca/parser/parse.go b/tosca/parser/parse.go index 65a3e3cb..6ab74f83 100644 --- a/tosca/parser/parse.go +++ b/tosca/parser/parse.go @@ -35,7 +35,7 @@ func (self *Context) Parse(url urlpkg.URL, stylist *terminal.Stylist, quirks tos serviceContext.AddHierarchies() // Phase 4: Inheritance - serviceContext.Inherit() + serviceContext.Inherit(nil) serviceContext.SetInputs(inputs) diff --git a/tosca/parser/phase1-read.go b/tosca/parser/phase1-read.go index 5260c9f7..2f0fb383 100644 --- a/tosca/parser/phase1-read.go +++ b/tosca/parser/phase1-read.go @@ -6,6 +6,7 @@ import ( "github.com/tliron/kutil/reflection" urlpkg "github.com/tliron/kutil/url" + "github.com/tliron/kutil/util" "github.com/tliron/puccini/tosca" "github.com/tliron/puccini/tosca/csar" "github.com/tliron/puccini/tosca/grammars" @@ -30,7 +31,7 @@ func (self *ServiceContext) ReadRoot(url urlpkg.URL, template string) bool { return ok } -func (self *ServiceContext) read(promise Promise, toscaContext *tosca.Context, container *Unit, nameTransformer tosca.NameTransformer, readerName string, template string) (*Unit, bool) { +func (self *ServiceContext) read(promise util.Promise, toscaContext *tosca.Context, container *Unit, nameTransformer tosca.NameTransformer, readerName string, template string) (*Unit, bool) { defer self.readWork.Done() if promise != nil { // For the goroutines waiting for our cached entityPtr @@ -119,10 +120,10 @@ func (self *ServiceContext) goReadImports(container *Unit) { continue } - promise := NewPromise() + promise := util.NewPromise() if cached, inCache := self.Context.readCache.LoadOrStore(key, promise); inCache { switch cached_ := cached.(type) { - case Promise: + case util.Promise: // Wait for promise logRead.Debugf("wait for promise: %s", key) self.readWork.Add(1) @@ -143,13 +144,13 @@ func (self *ServiceContext) goReadImports(container *Unit) { } } -func (self *ServiceContext) waitForPromise(promise Promise, key string, container *Unit, nameTransformer tosca.NameTransformer) { +func (self *ServiceContext) waitForPromise(promise util.Promise, key string, container *Unit, nameTransformer tosca.NameTransformer) { defer self.readWork.Done() promise.Wait() if cached, inCache := self.Context.readCache.Load(key); inCache { switch cached.(type) { - case Promise: + case util.Promise: logRead.Debugf("promise broken: %s", key) default: // entityPtr diff --git a/tosca/parser/phase2.1-namespaces.go b/tosca/parser/phase2.1-namespaces.go index 0e9887ae..84236f5f 100644 --- a/tosca/parser/phase2.1-namespaces.go +++ b/tosca/parser/phase2.1-namespaces.go @@ -6,20 +6,20 @@ import ( ) func (self *ServiceContext) AddNamespaces() { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() - self.Root.MergeNamespaces() + self.Root.mergeNamespaces() } -func (self *Unit) MergeNamespaces() { +func (self *Unit) mergeNamespaces() { context := self.GetContext() self.importsLock.RLock() defer self.importsLock.RUnlock() for _, import_ := range self.Imports { - import_.MergeNamespaces() + import_.mergeNamespaces() context.Namespace.Merge(import_.GetContext().Namespace, import_.NameTransformer) context.ScriptletNamespace.Merge(import_.GetContext().ScriptletNamespace) } diff --git a/tosca/parser/phase2.2-lookup.go b/tosca/parser/phase2.2-lookup.go index 045fb7d4..a38efff8 100644 --- a/tosca/parser/phase2.2-lookup.go +++ b/tosca/parser/phase2.2-lookup.go @@ -10,15 +10,15 @@ import ( ) func (self *ServiceContext) LookupNames() { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() - self.TraverseEntities(logLookup, self.Context.lookupFieldsWork, self.LookupFields) + self.TraverseEntities(logLookup, self.Context.lookupFieldsWork, self.lookupFields) } // From "lookup" tags // reflection.EntityTraverser signature -func (self *ServiceContext) LookupFields(entityPtr tosca.EntityPtr) bool { +func (self *ServiceContext) lookupFields(entityPtr tosca.EntityPtr) bool { lookupProblems := make(LookupProblems) context := tosca.GetContext(entityPtr) diff --git a/tosca/parser/phase3-hierarchies.go b/tosca/parser/phase3-hierarchies.go index 3fc177d2..ae24191a 100644 --- a/tosca/parser/phase3-hierarchies.go +++ b/tosca/parser/phase3-hierarchies.go @@ -1,25 +1,26 @@ package parser import ( + "github.com/tliron/kutil/reflection" "github.com/tliron/kutil/terminal" "github.com/tliron/puccini/tosca" ) func (self *ServiceContext) AddHierarchies() { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() - self.Root.MergeHierarchies(make(tosca.HierarchyContext), self.Context.addHierarchyWork) + self.Root.mergeHierarchies(make(tosca.HierarchyContext), self.Context.addHierarchyWork) } -func (self *Unit) MergeHierarchies(hierarchyContext tosca.HierarchyContext, work tosca.EntityWork) { +func (self *Unit) mergeHierarchies(hierarchyContext tosca.HierarchyContext, work reflection.EntityWork) { context := self.GetContext() self.importsLock.RLock() defer self.importsLock.RUnlock() for _, import_ := range self.Imports { - import_.MergeHierarchies(hierarchyContext, work) + import_.mergeHierarchies(hierarchyContext, work) context.Hierarchy.Merge(import_.GetContext().Hierarchy, hierarchyContext) } diff --git a/tosca/parser/phase4-inheritance.go b/tosca/parser/phase4-inheritance.go index 4a2a8344..20c5cb31 100644 --- a/tosca/parser/phase4-inheritance.go +++ b/tosca/parser/phase4-inheritance.go @@ -9,15 +9,18 @@ import ( "github.com/tliron/puccini/tosca" ) -func (self *ServiceContext) Inherit() { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() +func (self *ServiceContext) Inherit(debug func(Tasks)) { + self.Context.lock.Lock() + defer self.Context.lock.Unlock() - tasks := self.GetInheritTasks() + tasks := self.getInheritTasks() + if debug != nil { + debug(tasks) + } tasks.Drain() } -func (self *ServiceContext) GetInheritTasks() Tasks { +func (self *ServiceContext) getInheritTasks() Tasks { inheritContext := NewInheritContext() self.TraverseEntities(logInheritance, self.Context.getInheritTaskWork, func(entityPtr tosca.EntityPtr) bool { inheritContext.GetInheritTask(entityPtr) diff --git a/tosca/parser/phase5-rendering.go b/tosca/parser/phase5-rendering.go index dac190ae..15e083e0 100644 --- a/tosca/parser/phase5-rendering.go +++ b/tosca/parser/phase5-rendering.go @@ -5,8 +5,8 @@ import ( ) func (self *ServiceContext) Render() tosca.EntityPtrs { - self.Context.entitiesLock.Lock() - defer self.Context.entitiesLock.Unlock() + self.Context.lock.Lock() + defer self.Context.lock.Unlock() var entityPtrs tosca.EntityPtrs diff --git a/tosca/parser/traverse.go b/tosca/parser/traverse.go index 9d6d55fa..351e8892 100644 --- a/tosca/parser/traverse.go +++ b/tosca/parser/traverse.go @@ -1,16 +1,14 @@ package parser import ( - "sync" - "github.com/tliron/kutil/logging" "github.com/tliron/kutil/reflection" "github.com/tliron/puccini/tosca" ) -func (self *ServiceContext) TraverseEntities(log logging.Logger, work tosca.EntityWork, traverse reflection.EntityTraverser) { +func (self *ServiceContext) TraverseEntities(log logging.Logger, work reflection.EntityWork, traverse reflection.EntityTraverser) { if work == nil { - work = make(tosca.EntityWork) + work = make(reflection.EntityWork) } // Root @@ -22,50 +20,3 @@ func (self *ServiceContext) TraverseEntities(log logging.Logger, work tosca.Enti return true }) } - -// -// Promise -// - -type Promise chan struct{} - -func NewPromise() Promise { - return make(Promise) -} - -func (self Promise) Release() { - close(self) -} - -func (self Promise) Wait() { - <-self -} - -// TODO: unused -// -// CoordinatedWork -// - -type CoordinatedWork struct { - sync.Map - Log logging.Logger -} - -func NewCoordinatedWork(log logging.Logger) *CoordinatedWork { - return &CoordinatedWork{ - Log: log, - } -} - -func (self *CoordinatedWork) Start(key string) (Promise, bool) { - promise := NewPromise() - if existing, loaded := self.LoadOrStore(key, promise); !loaded { - self.Log.Debugf("start: %s", key) - return promise, true - } else { - self.Log.Debugf("wait for: %s", key) - promise = existing.(Promise) - promise.Wait() - return nil, false - } -}