internal/dis: Rework initialization

This commit is contained in:
Tom Wiesing 2022-10-04 15:21:57 +02:00
parent 4358320433
commit abc985892a
No known key found for this signature in database
4 changed files with 173 additions and 156 deletions

View file

@ -31,16 +31,6 @@ func (p *Pool) init() {
}) })
} }
// 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 {
return cc
}
}
panic("FindComponent: Invalid arguments")
}
// InitComponent initializes a specific component and caches it within the given pool. // InitComponent initializes a specific component and caches it within the given pool.
// //
// Concurrent calls of InitComponent must use a distinct thread parameter. // Concurrent calls of InitComponent must use a distinct thread parameter.
@ -54,7 +44,7 @@ func InitComponent[C Component](p *Pool, thread int32, core Core, init func(comp
defer p.rLock.Unlock() defer p.rLock.Unlock()
// get a description of the type // get a description of the type
cd := getComponent[C]() cd := GetMeta[C]()
// find a field to put the component into // find a field to put the component into
instance, created := func() (C, bool) { instance, created := func() (C, bool) {
@ -111,25 +101,24 @@ func InitComponent[C Component](p *Pool, thread int32, core Core, init func(comp
return instance return instance
} }
// getComponent gets the component belonging to a component type // GetMeta gets the component belonging to a component type
func getComponent[C Component]() (desc component) { func GetMeta[C Component]() (meta Meta) {
tp := reflectx.TypeOf[C]() tp := reflectx.TypeOf[C]()
if tp.Kind() != reflect.Pointer { if tp.Kind() != reflect.Pointer {
panic("getComponent: C must be backed by a pointer") panic("GetMeta: C must be backed by a pointer (" + tp.String() + ")")
// should never be reached!
} }
desc.Elem = tp.Elem() meta.Elem = tp.Elem()
desc.Name = desc.Elem.PkgPath() + "." + desc.Elem.Name() meta.Name = meta.Elem.PkgPath() + "." + meta.Elem.Name()
return return
} }
// component represents a component // Meta represents meta information about a component
type component struct { type Meta struct {
Elem reflect.Type // the element type of the component Elem reflect.Type // the element type of the component
Name string // the name of the component Name string // the name of the component
} }
// New creates a new ComponentDescription // New creates a new ComponentDescription
func (cd component) New() any { func (cd Meta) New() any {
return reflect.New(cd.Elem).Interface() return reflect.New(cd.Elem).Interface()
} }

View file

@ -1,6 +1,7 @@
package dis package dis
import ( import (
"reflect"
"sync/atomic" "sync/atomic"
"time" "time"
@ -14,6 +15,8 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/component/triplestore" "github.com/FAU-CDI/wisski-distillery/internal/component/triplestore"
"github.com/FAU-CDI/wisski-distillery/internal/component/web" "github.com/FAU-CDI/wisski-distillery/internal/component/web"
"github.com/FAU-CDI/wisski-distillery/internal/core" "github.com/FAU-CDI/wisski-distillery/internal/core"
"github.com/FAU-CDI/wisski-distillery/pkg/slicesx"
"github.com/tkw1536/goprogram/lib/reflectx"
) )
// components holds the various components of the distillery // components holds the various components of the distillery
@ -25,150 +28,160 @@ type components struct {
pool component.Pool pool component.Pool
} }
// c initializes a component of the provided type // register returns all components of the distillery
func c[C component.Component](dis *Distillery, thread int32, init func(component C, thread int32)) C { func register(dis *Distillery, thread int32) []component.Component {
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
//
func (c *components) thread() int32 {
return atomic.AddInt32(&c.t, 1)
}
func (dis *Distillery) cWeb(thread int32) *web.Web {
return component.InitComponent[*web.Web](&dis.pool, thread, dis.Core, nil)
}
func (dis *Distillery) cControl(thread int32) *control.Control {
return component.InitComponent(&dis.pool, thread, dis.Core, func(control *control.Control, thread int32) {
control.ResolverFile = core.PrefixConfig
control.Servables = dis.cServables(thread)
})
}
func (dis *Distillery) cSSH(thread int32) *ssh.SSH {
return component.InitComponent[*ssh.SSH](&dis.pool, thread, dis.Core, nil)
}
func (dis *Distillery) cSQL(thread int32) *sql.SQL {
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
})
}
func (dis *Distillery) cTriplestore(thread int32) *triplestore.Triplestore {
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
})
}
func (dis *Distillery) cInstances(thread int32) *instances.Instances {
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.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)
snapshots.Backupable = dis.cBackupable(thread)
})
}
func (dis *Distillery) cResolver(thread int32) *resolver.Resolver {
return component.InitComponent(&dis.pool, thread, dis.Core, func(resolver *resolver.Resolver, thread int32) {
resolver.Control = dis.cControl(thread)
resolver.ResolverFile = core.PrefixConfig
})
}
//
// ALL COMPONENTS
//
func (dis *Distillery) cComponents(thread int32) []component.Component {
return []component.Component{ return []component.Component{
dis.cWeb(thread), ra[*web.Web](dis, thread),
dis.cSSH(thread),
dis.cTriplestore(thread), ra[*ssh.SSH](dis, thread),
dis.cSQL(thread),
dis.cInstances(thread), r(dis, thread, func(ts *triplestore.Triplestore) {
ts.BaseURL = "http://" + dis.Upstream.Triplestore
ts.PollContext = dis.Context()
ts.PollInterval = time.Second
}),
r(dis, thread, func(sql *sql.SQL) {
sql.ServerURL = dis.Upstream.SQL
sql.PollContext = dis.Context()
sql.PollInterval = time.Second
}),
ra[*instances.Instances](dis, thread),
// Snapshots // Snapshots
dis.cSnapshotManager(thread), ra[*snapshots.Manager](dis, thread),
cc[*snapshots.Config](dis, thread), ra[*snapshots.Config](dis, thread),
cc[*snapshots.Bookkeeping](dis, thread), ra[*snapshots.Bookkeeping](dis, thread),
cc[*snapshots.Filesystem](dis, thread), ra[*snapshots.Filesystem](dis, thread),
c(dis, thread, func(pbs *snapshots.Pathbuilders, thread int32) { ra[*snapshots.Pathbuilders](dis, thread),
pbs.Instances = dis.cInstances(thread)
}),
// Control server // Control server
dis.cControl(thread), r(dis, thread, func(control *control.Control) {
c(dis, thread, func(sh *control.SelfHandler, thread int32) { control.ResolverFile = core.PrefixConfig
sh.Instances = dis.cInstances(thread)
}), }),
dis.cResolver(thread), ra[*control.SelfHandler](dis, thread),
c(dis, thread, func(info *control.Info, thread int32) { r(dis, thread, func(resolver *resolver.Resolver) {
info.Instances = dis.cInstances(thread) resolver.ResolverFile = core.PrefixConfig
}), }),
ra[*control.Info](dis, thread),
} }
} }
// // r initializes a component from the provided distillery.
// COMPONENT SUBTYPE GETTERS func r[C component.Component](dis *Distillery, thread int32, init func(component C)) C {
// return component.InitComponent(&dis.pool, thread, dis.Core, makeInitFunction(dis, init))
func (dis *Distillery) cInstallables(thread int32) []component.Installable {
return getComponentSubtype[component.Installable](dis, thread)
} }
func (dis *Distillery) cUpdateable(thread int32) []component.Updatable { // ra is like r, but does not provided additional initialization
return getComponentSubtype[component.Updatable](dis, thread) func ra[C component.Component](dis *Distillery, thread int32) C {
return r[C](dis, thread, nil)
} }
func (dis *Distillery) cBackupable(thread int32) []component.Backupable { var componentType = reflectx.TypeOf[component.Component]()
return getComponentSubtype[component.Backupable](dis, thread)
}
func (dis *Distillery) cProvisionable(thread int32) []component.Provisionable { // makeInitFunction generate an init function for a specific component.
return getComponentSubtype[component.Provisionable](dis, thread) // The function should be called at most once.
} func makeInitFunction[C component.Component](dis *Distillery, rest func(instance C)) func(instance C, thread int32) {
return func(instance C, thread int32) {
meta := component.GetMeta[C]()
func (dis *Distillery) cSnapshotable(thread int32) []component.Snapshotable { // this function automatically initializes component.Component-like fields of the instance.
return getComponentSubtype[component.Snapshotable](dis, thread) // for this we first store two types of fields:
}
func (dis *Distillery) cServables(thread int32) []component.Servable { singles := make(map[string]reflect.Type) // fields of type C where C is a component.Type
return getComponentSubtype[component.Servable](dis, thread) multis := make(map[string]reflect.Type) // fields of type []C where C is an interface that implements component.
}
func getComponentSubtype[C component.Component](dis *Distillery, thread int32) (components []C) { // fill the above variables, with a mapping of field name to struct
all := dis.cComponents(thread) count := meta.Elem.NumField()
for i := 0; i < count; i++ {
field := meta.Elem.Field(i)
components = make([]C, 0, len(all)) name := field.Name
for _, c := range all { tp := field.Type
sc, ok := c.(C)
if !ok { switch {
continue // field is a `Component``
case tp.Implements(componentType):
singles[name] = tp
// field is a `[]Component``
case tp.Kind() == reflect.Slice && tp.Elem().Kind() == reflect.Interface && tp.Elem().Implements(componentType):
multis[name] = tp.Elem()
}
} }
components = append(components, sc)
}
return // do the rest of the initialization
if rest != nil {
defer rest(instance)
}
if len(singles) == 0 && len(multis) == 0 {
// no fields to assign, bail out immediatly
return
}
registry := register(dis, thread)
instanceV := reflect.ValueOf(instance).Elem()
// assign the component fields
for field, eType := range singles {
c := slicesx.First(registry, func(c component.Component) bool {
return reflect.TypeOf(c).AssignableTo(eType)
})
field := instanceV.FieldByName(field)
field.Set(reflect.ValueOf(c))
}
// assign the multi subtypes
registryR := reflect.ValueOf(registry)
for field, eType := range multis {
cs := filterSubtype(registryR, eType)
field := instanceV.FieldByName(field)
field.Set(cs)
}
}
}
// filterSubtype filters the slice of type []S into a slice of type []iface.
// S and I must be interface types.
func filterSubtype(sliceS reflect.Value, iface reflect.Type) reflect.Value {
len := sliceS.Len()
// convert each element
result := reflect.MakeSlice(reflect.SliceOf(iface), 0, len)
for i := 0; i < len; i++ {
element := sliceS.Index(i)
if element.Type().Implements(iface) {
result = reflect.Append(result, element.Elem().Convert(iface))
}
}
return result
}
//
// Export Components
//
// ea exports all components of the given subtype
func ea[C component.Component](dis *Distillery) []C {
registry := register(dis, atomic.AddInt32(&dis.t, 1))
results := make([]C, 0, len(registry))
for _, comp := range registry {
if cc, ok := comp.(C); ok {
results = append(results, cc)
}
}
return results
}
// e exports a single component of the given subtype
func e[C component.Component](dis *Distillery) C {
for _, comp := range register(dis, atomic.AddInt32(&dis.t, 1)) {
if cc, ok := comp.(C); ok {
return cc
}
}
panic("e: component is missing")
} }

View file

@ -48,30 +48,33 @@ func (dis *Distillery) Context() context.Context {
// //
func (dis *Distillery) Control() *control.Control { func (dis *Distillery) Control() *control.Control {
return dis.cControl(dis.thread()) return e[*control.Control](dis)
} }
func (dis *Distillery) Resolver() *resolver.Resolver { func (dis *Distillery) Resolver() *resolver.Resolver {
return dis.cResolver(dis.thread()) return e[*resolver.Resolver](dis)
} }
func (dis *Distillery) SSH() *ssh.SSH { func (dis *Distillery) SSH() *ssh.SSH {
return dis.cSSH(dis.thread()) return e[*ssh.SSH](dis)
} }
func (dis *Distillery) SQL() *sql.SQL { func (dis *Distillery) SQL() *sql.SQL {
return dis.cSQL(dis.thread()) return e[*sql.SQL](dis)
} }
func (dis *Distillery) Triplestore() *triplestore.Triplestore { func (dis *Distillery) Triplestore() *triplestore.Triplestore {
return dis.cTriplestore(dis.thread()) return e[*triplestore.Triplestore](dis)
} }
func (dis *Distillery) Instances() *instances.Instances { func (dis *Distillery) Instances() *instances.Instances {
return dis.cInstances(dis.thread()) return e[*instances.Instances](dis)
} }
func (dis *Distillery) SnapshotManager() *snapshots.Manager { func (dis *Distillery) SnapshotManager() *snapshots.Manager {
return dis.cSnapshotManager(dis.thread()) return e[*snapshots.Manager](dis)
} }
func (dis *Distillery) Installable() []component.Installable { return dis.cInstallables(dis.thread()) } func (dis *Distillery) Installable() []component.Installable {
func (dis *Distillery) Updatable() []component.Updatable { return dis.cUpdateable(dis.thread()) } return ea[component.Installable](dis)
}
func (dis *Distillery) Updatable() []component.Updatable {
return ea[component.Updatable](dis)
}
func (dis *Distillery) Provisionable() []component.Provisionable { func (dis *Distillery) Provisionable() []component.Provisionable {
return dis.cProvisionable(dis.thread()) return ea[component.Provisionable](dis)
} }

View file

@ -16,6 +16,18 @@ func ForSorted[K constraints.Ordered, V any](mp map[K]V, callback func(k K, v V)
} }
} }
// First returns the first value of type V
// When no such value exists, returns the zero value
func First[V any](values []V, test func(v V) bool) V {
for _, v := range values {
if test(v) {
return v
}
}
var v V
return v
}
// Any returns true if test returns true for any of values. // Any returns true if test returns true for any of values.
func Any[T any](values []T, test func(T) bool) bool { func Any[T any](values []T, test func(T) bool) bool {
for _, v := range values { for _, v := range values {