diff --git a/internal/component/extras/extras.go b/internal/component/extras/extras.go deleted file mode 100644 index f6c0570..0000000 --- a/internal/component/extras/extras.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package extras implements additional components to be used for backups and snapshots -package extras diff --git a/internal/component/pool.go b/internal/component/pool.go index 74cea2a..038882f 100644 --- a/internal/component/pool.go +++ b/internal/component/pool.go @@ -31,29 +31,7 @@ func (p *Pool) init() { }) } -// ComponentDescription describes a component -type ComponentDescription struct { - Type reflect.Type - Elem reflect.Type - Name string -} - -// New creates a new ComponentDescription -func (cd ComponentDescription) New() any { - return reflect.New(cd.Elem).Interface() -} - -// GetDescription gets the description of a component type -func GetDescription[C Component]() (desc ComponentDescription) { - desc.Type = reflectx.TypeOf[C]() - if desc.Type.Kind() != reflect.Pointer { - panic("GetDescription: C must be backed by a pointer") - } - desc.Elem = desc.Type.Elem() - desc.Name = desc.Elem.PkgPath() + "." + desc.Elem.Name() - return -} - +// Find finds all components of the specific subtype func Find[C Component](components []Component) C { for _, c := range components { if cc, ok := c.(C); ok { @@ -63,17 +41,20 @@ func Find[C Component](components []Component) C { panic("FindComponent: Invalid arguments") } -// Put initializes a single component in the pool. +// InitComponent initializes a specific component and caches it within the given pool. +// +// Concurrent calls of InitComponent must use a distinct thread parameter. +// Nested calls of InitComponent should use the same thread parameter. // // Init may initialize components, but not call functions on them! -func PutComponent[C Component](p *Pool, thread int32, core Core, init func(component C, thread int32)) C { +func InitComponent[C Component](p *Pool, thread int32, core Core, init func(component C, thread int32)) C { p.init() p.rLock.Lock(int(thread)) defer p.rLock.Unlock() // get a description of the type - cd := GetDescription[C]() + cd := getComponent[C]() // find a field to put the component into instance, created := func() (C, bool) { @@ -129,3 +110,26 @@ func PutComponent[C Component](p *Pool, thread int32, core Core, init func(compo // and return the instance return instance } + +// getComponent gets the component belonging to a component type +func getComponent[C Component]() (desc component) { + tp := reflectx.TypeOf[C]() + if tp.Kind() != reflect.Pointer { + panic("getComponent: C must be backed by a pointer") + // should never be reached! + } + desc.Elem = tp.Elem() + desc.Name = desc.Elem.PkgPath() + "." + desc.Elem.Name() + return +} + +// component represents a component +type component struct { + Elem reflect.Type // the element type of the component + Name string // the name of the component +} + +// New creates a new ComponentDescription +func (cd component) New() any { + return reflect.New(cd.Elem).Interface() +} diff --git a/internal/component/extras/bookkeeping.go b/internal/component/snapshots/extras_bookkeeping.go similarity index 97% rename from internal/component/extras/bookkeeping.go rename to internal/component/snapshots/extras_bookkeeping.go index 31d0417..fcf79fe 100644 --- a/internal/component/extras/bookkeeping.go +++ b/internal/component/snapshots/extras_bookkeeping.go @@ -1,4 +1,4 @@ -package extras +package snapshots import ( "fmt" diff --git a/internal/component/extras/config.go b/internal/component/snapshots/extras_config.go similarity index 98% rename from internal/component/extras/config.go rename to internal/component/snapshots/extras_config.go index 0a8a7d6..138e6d1 100644 --- a/internal/component/extras/config.go +++ b/internal/component/snapshots/extras_config.go @@ -1,4 +1,4 @@ -package extras +package snapshots import ( "path/filepath" diff --git a/internal/component/extras/filesystem.go b/internal/component/snapshots/extras_filesystem.go similarity index 97% rename from internal/component/extras/filesystem.go rename to internal/component/snapshots/extras_filesystem.go index dbda83e..d348d67 100644 --- a/internal/component/extras/filesystem.go +++ b/internal/component/snapshots/extras_filesystem.go @@ -1,4 +1,4 @@ -package extras +package snapshots import ( "github.com/FAU-CDI/wisski-distillery/internal/component" diff --git a/internal/component/extras/pathbuilders.go b/internal/component/snapshots/extras_pathbuilders.go similarity index 98% rename from internal/component/extras/pathbuilders.go rename to internal/component/snapshots/extras_pathbuilders.go index b868dff..7123788 100644 --- a/internal/component/extras/pathbuilders.go +++ b/internal/component/snapshots/extras_pathbuilders.go @@ -1,4 +1,4 @@ -package extras +package snapshots import ( "io" diff --git a/internal/dis/component.go b/internal/dis/component.go index 9ba2296..52f2368 100644 --- a/internal/dis/component.go +++ b/internal/dis/component.go @@ -6,7 +6,6 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/component" "github.com/FAU-CDI/wisski-distillery/internal/component/control" - "github.com/FAU-CDI/wisski-distillery/internal/component/extras" "github.com/FAU-CDI/wisski-distillery/internal/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/component/snapshots" "github.com/FAU-CDI/wisski-distillery/internal/component/sql" @@ -25,6 +24,16 @@ type components struct { pool component.Pool } +// c initializes a component of the provided type +func c[C component.Component](dis *Distillery, thread int32, init func(component C, thread int32)) C { + return component.InitComponent(&dis.pool, thread, dis.Core, init) +} + +// cc is like c, but with init set to nil +func cc[C component.Component](dis *Distillery, thread int32) C { + return c[C](dis, thread, nil) +} + // // Individual Components // @@ -34,22 +43,22 @@ func (c *components) thread() int32 { } func (dis *Distillery) cWeb(thread int32) *web.Web { - return component.PutComponent[*web.Web](&dis.pool, thread, dis.Core, nil) + return component.InitComponent[*web.Web](&dis.pool, thread, dis.Core, nil) } func (dis *Distillery) cControl(thread int32) *control.Control { - return component.PutComponent(&dis.pool, thread, dis.Core, func(control *control.Control, thread int32) { + return component.InitComponent(&dis.pool, thread, dis.Core, func(control *control.Control, thread int32) { control.ResolverFile = core.PrefixConfig control.Instances = dis.cInstances(thread) }) } func (dis *Distillery) cSSH(thread int32) *ssh.SSH { - return component.PutComponent[*ssh.SSH](&dis.pool, thread, dis.Core, nil) + return component.InitComponent[*ssh.SSH](&dis.pool, thread, dis.Core, nil) } func (dis *Distillery) cSQL(thread int32) *sql.SQL { - return component.PutComponent(&dis.pool, thread, dis.Core, func(sql *sql.SQL, thread int32) { + return component.InitComponent(&dis.pool, thread, dis.Core, func(sql *sql.SQL, thread int32) { sql.ServerURL = dis.Upstream.SQL sql.PollContext = dis.Context() sql.PollInterval = time.Second @@ -57,7 +66,7 @@ func (dis *Distillery) cSQL(thread int32) *sql.SQL { } func (dis *Distillery) cTriplestore(thread int32) *triplestore.Triplestore { - return component.PutComponent(&dis.pool, thread, dis.Core, func(ts *triplestore.Triplestore, thread int32) { + return component.InitComponent(&dis.pool, thread, dis.Core, func(ts *triplestore.Triplestore, thread int32) { ts.BaseURL = "http://" + dis.Upstream.Triplestore ts.PollContext = dis.Context() ts.PollInterval = time.Second @@ -65,14 +74,14 @@ func (dis *Distillery) cTriplestore(thread int32) *triplestore.Triplestore { } func (dis *Distillery) cInstances(thread int32) *instances.Instances { - return component.PutComponent(&dis.pool, thread, dis.Core, func(instances *instances.Instances, thread int32) { + return component.InitComponent(&dis.pool, thread, dis.Core, func(instances *instances.Instances, thread int32) { instances.SQL = dis.cSQL(thread) instances.TS = dis.cTriplestore(thread) }) } func (dis *Distillery) cSnapshotManager(thread int32) *snapshots.Manager { - return component.PutComponent(&dis.pool, thread, dis.Core, func(snapshots *snapshots.Manager, thread int32) { + return component.InitComponent(&dis.pool, thread, dis.Core, func(snapshots *snapshots.Manager, thread int32) { snapshots.SQL = dis.cSQL(thread) snapshots.Instances = dis.cInstances(thread) snapshots.Snapshotable = dis.cSnapshotable(thread) @@ -80,28 +89,6 @@ func (dis *Distillery) cSnapshotManager(thread int32) *snapshots.Manager { }) } -// -// EXTRAS COMPONENTS -// - -func (dis *Distillery) cExtrasConfig(thread int32) *extras.Config { - return component.PutComponent[*extras.Config](&dis.pool, thread, dis.Core, nil) -} - -func (dis *Distillery) cExtrasBookkeeping(thread int32) *extras.Bookkeeping { - return component.PutComponent[*extras.Bookkeeping](&dis.pool, thread, dis.Core, nil) -} - -func (dis *Distillery) cExtrasFilesystem(thread int32) *extras.Filesystem { - return component.PutComponent[*extras.Filesystem](&dis.pool, thread, dis.Core, nil) -} - -func (dis *Distillery) cExtrasPathbuilders(thread int32) *extras.Pathbuilders { - return component.PutComponent(&dis.pool, thread, dis.Core, func(pbs *extras.Pathbuilders, thread int32) { - pbs.Instances = dis.cInstances(thread) - }) -} - // // ALL COMPONENTS // @@ -114,12 +101,15 @@ func (dis *Distillery) cComponents(thread int32) []component.Component { dis.cTriplestore(thread), dis.cSQL(thread), dis.cInstances(thread), - dis.cSnapshotManager(thread), - dis.cExtrasConfig(thread), - dis.cExtrasBookkeeping(thread), - dis.cExtrasFilesystem(thread), - dis.cExtrasPathbuilders(thread), + // Snapshots + dis.cSnapshotManager(thread), + cc[*snapshots.Config](dis, thread), + cc[*snapshots.Bookkeeping](dis, thread), + cc[*snapshots.Filesystem](dis, thread), + c(dis, thread, func(pbs *snapshots.Pathbuilders, thread int32) { + pbs.Instances = dis.cInstances(thread) + }), } } @@ -147,12 +137,12 @@ func (dis *Distillery) cSnapshotable(thread int32) []component.Snapshotable { return getComponentSubtype[component.Snapshotable](dis, thread) } -func getComponentSubtype[T component.Component](dis *Distillery, thread int32) (components []T) { +func getComponentSubtype[C component.Component](dis *Distillery, thread int32) (components []C) { all := dis.cComponents(thread) - components = make([]T, 0, len(all)) + components = make([]C, 0, len(all)) for _, c := range all { - sc, ok := c.(T) + sc, ok := c.(C) if !ok { continue } diff --git a/internal/dis/distillery.go b/internal/dis/distillery.go index 914c085..f347bdd 100644 --- a/internal/dis/distillery.go +++ b/internal/dis/distillery.go @@ -1,3 +1,4 @@ +// Package dis provides the main distillery package dis import ( diff --git a/pkg/rlock/rlock.go b/pkg/rlock/rlock.go index efb4bdd..49c4ea6 100644 --- a/pkg/rlock/rlock.go +++ b/pkg/rlock/rlock.go @@ -5,6 +5,7 @@ import ( "time" ) +// RLock is like [sync.Mutex], but permits recursive locking. type RLock struct { m sync.Mutex // m is held internally @@ -13,32 +14,40 @@ type RLock struct { counter uint64 } +// Lock acquires this lock with the given id, and blocks until it can be aquired. +// Concurrent locks with the same ids do not block; however each should be unlocked with a call to unlock. func (rm *RLock) Lock(id int) { +loop: for { rm.m.Lock() - if !rm.held { + switch { + case !rm.held: rm.held = true rm.holder = id - break - } else if rm.held && rm.holder == id { - break - } else { - rm.m.Unlock() - time.Sleep(time.Millisecond) - continue + break loop + case rm.held && rm.holder == id: + break loop } + rm.m.Unlock() + time.Sleep(time.Millisecond) // spinning! } rm.counter++ rm.m.Unlock() } +// Unlock releases the lock func (rm *RLock) Unlock() { rm.m.Lock() + defer rm.m.Unlock() + + if !rm.held || rm.counter <= 0 { + panic("RLock: Unlock() without Lock()") + } + rm.counter-- if rm.counter == 0 { rm.held = false rm.holder = 0 } - rm.m.Unlock() }