internal/component: Further optimize initialization
This commit is contained in:
parent
abc985892a
commit
84dcaa62a9
6 changed files with 280 additions and 294 deletions
|
|
@ -3,15 +3,12 @@ package component
|
||||||
|
|
||||||
// Component represents a logical subsystem of the distillery.
|
// Component represents a logical subsystem of the distillery.
|
||||||
// Every component must embed [ComponentBase] and should be initialized using [Initialize].
|
// Every component must embed [ComponentBase] and should be initialized using [Initialize].
|
||||||
|
// A Component should be implemented as a pointer to a struct.
|
||||||
//
|
//
|
||||||
// By convention these are defined within their corresponding subpackage.
|
// By convention these are defined within their corresponding subpackage.
|
||||||
// This subpackage also contains all required resources.
|
// This subpackage also contains all required resources.
|
||||||
// Furthermore, a component is typically instantiated using a call on the ["distillery.Distillery"] struct.
|
|
||||||
//
|
//
|
||||||
// For example, the web.Web component lives in the web package and can be created like:
|
// Components are initialized using a [Pool].
|
||||||
//
|
|
||||||
// var dis Distillery
|
|
||||||
// web := dis.Web()
|
|
||||||
type Component interface {
|
type Component interface {
|
||||||
// Name returns the name of this component.
|
// Name returns the name of this component.
|
||||||
// It should correspond to the appropriate subpackage.
|
// It should correspond to the appropriate subpackage.
|
||||||
|
|
|
||||||
|
|
@ -2,123 +2,146 @@ package component
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/rlock"
|
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
||||||
"github.com/tkw1536/goprogram/lib/reflectx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pool represents a pool of components
|
// Pool holds a pool of components and provides factilities to create and access them.
|
||||||
|
// See [Pool.All], [ExportComponents] and [ExportComponent].
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
rLock rlock.RLock
|
all lazy.Lazy[[]Component] // all components
|
||||||
|
|
||||||
// the actual queue of initi functions!
|
|
||||||
nested uint64 // is the q active?
|
|
||||||
queue []func(thread int32)
|
|
||||||
|
|
||||||
// global initalization!
|
|
||||||
initOnce sync.Once
|
|
||||||
|
|
||||||
// components and lock!
|
|
||||||
cLock sync.Mutex
|
|
||||||
components map[string]Component
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Pool) init() {
|
// All initializes or returns all components stored in this pool.
|
||||||
p.initOnce.Do(func() {
|
//
|
||||||
p.components = make(map[string]Component)
|
// The All function should return an array of calls to [Make] with the provided context.
|
||||||
|
// Multiple calls to the this method return the same return value.
|
||||||
|
func (p *Pool) All(All func(context *PoolContext) []Component) []Component {
|
||||||
|
return p.all.Get(func() []Component {
|
||||||
|
// create a new context
|
||||||
|
context := &PoolContext{
|
||||||
|
all: All,
|
||||||
|
cache: make(map[string]Component),
|
||||||
|
}
|
||||||
|
|
||||||
|
// and process them all
|
||||||
|
all := context.all(context)
|
||||||
|
context.process(all)
|
||||||
|
return all
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitComponent initializes a specific component and caches it within the given pool.
|
// PoolContext is a context used during [Make].
|
||||||
//
|
// It should not be initialized by a user.
|
||||||
// Concurrent calls of InitComponent must use a distinct thread parameter.
|
type PoolContext struct {
|
||||||
// Nested calls of InitComponent should use the same thread parameter.
|
all func(context *PoolContext) []Component // function to return all components
|
||||||
//
|
|
||||||
// Init may initialize components, but not call functions on them!
|
|
||||||
func InitComponent[C Component](p *Pool, thread int32, core Core, init func(component C, thread int32)) C {
|
|
||||||
p.init()
|
|
||||||
|
|
||||||
p.rLock.Lock(int(thread))
|
cache map[string]Component // cached components
|
||||||
defer p.rLock.Unlock()
|
queue []delayedInit // init queue
|
||||||
|
}
|
||||||
|
|
||||||
// get a description of the type
|
type delayedInit struct {
|
||||||
cd := GetMeta[C]()
|
meta meta
|
||||||
|
value reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
// find a field to put the component into
|
func (di delayedInit) Do(all []Component) {
|
||||||
instance, created := func() (C, bool) {
|
di.meta.InitComponent(di.value, all)
|
||||||
p.cLock.Lock()
|
}
|
||||||
defer p.cLock.Unlock()
|
|
||||||
|
|
||||||
// create the component
|
// process processes the queue in this process
|
||||||
field, ok := p.components[cd.Name]
|
func (p *PoolContext) process(all []Component) {
|
||||||
if ok {
|
|
||||||
return field.(C), false
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new component
|
|
||||||
p.components[cd.Name] = cd.New().(Component)
|
|
||||||
return p.components[cd.Name].(C), true
|
|
||||||
}()
|
|
||||||
|
|
||||||
// if we already created the instance, then there is nothing to do
|
|
||||||
// as someone else will init it!
|
|
||||||
if !created {
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup the core initialization now!
|
|
||||||
instance.getBase().Core = core
|
|
||||||
|
|
||||||
if init == nil {
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are in nested mode, then delay the init!
|
|
||||||
if !atomic.CompareAndSwapUint64(&p.nested, 0, 1) {
|
|
||||||
func() {
|
|
||||||
p.queue = append(p.queue, func(thread int32) {
|
|
||||||
init(instance, thread)
|
|
||||||
})
|
|
||||||
}()
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
defer atomic.StoreUint64(&p.nested, 0)
|
|
||||||
|
|
||||||
// init ourselves first (everything below will be nested)
|
|
||||||
init(instance, thread)
|
|
||||||
|
|
||||||
// do all the delayed initializations
|
|
||||||
index := 0
|
index := 0
|
||||||
for len(p.queue) > index {
|
for len(p.queue) > index {
|
||||||
p.queue[index](thread)
|
p.queue[index].Do(all)
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
p.queue = nil
|
p.queue = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make creates or returns a cached component of the given Context.
|
||||||
|
//
|
||||||
|
// Components are initialized by first calling the init function.
|
||||||
|
// Then all component-like fields of fields are filled with their appropriate components.
|
||||||
|
//
|
||||||
|
// A component-like field has one of the following types:
|
||||||
|
//
|
||||||
|
// - A pointer to a struct type that implements component
|
||||||
|
// - A slice type of an interface type that implements component
|
||||||
|
//
|
||||||
|
// These fields are initialized in an undefined order during initialization.
|
||||||
|
// The init function may not rely on these existing.
|
||||||
|
// Furthermore, the init function may not cause other components to be initialized.
|
||||||
|
//
|
||||||
|
// The init function may be nil, indicating that no additional initialization is required.
|
||||||
|
func Make[C Component](context *PoolContext, core Core, init func(component C)) C {
|
||||||
|
// get a description of the type
|
||||||
|
cd := getMeta[C]()
|
||||||
|
|
||||||
|
// if an instance already exists, return it!
|
||||||
|
if instance, ok := context.cache[cd.Name]; ok {
|
||||||
|
return instance.(C)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that we have an array of components
|
||||||
|
if context.cache == nil {
|
||||||
|
context.cache = make(map[string]Component)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a fresh (empty) instance
|
||||||
|
context.cache[cd.Name] = cd.New()
|
||||||
|
instance := context.cache[cd.Name].(C)
|
||||||
|
|
||||||
|
// do the core and self-initialization
|
||||||
|
instance.getBase().Core = core
|
||||||
|
|
||||||
|
if init != nil {
|
||||||
|
init(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cd.NeedsInitComponent() {
|
||||||
|
context.queue = append(context.queue, delayedInit{
|
||||||
|
meta: cd,
|
||||||
|
value: reflect.ValueOf(instance),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// and return the instance
|
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMeta gets the component belonging to a component type
|
//
|
||||||
func GetMeta[C Component]() (meta Meta) {
|
// PUBLIC FUNCTIONS
|
||||||
tp := reflectx.TypeOf[C]()
|
//
|
||||||
if tp.Kind() != reflect.Pointer {
|
|
||||||
panic("GetMeta: C must be backed by a pointer (" + tp.String() + ")")
|
// ExportComponents exports all components that are a C from the pool.
|
||||||
|
//
|
||||||
|
// All should be the function of the core that initializes all components.
|
||||||
|
// All should only make calls to [InitComponent].
|
||||||
|
func ExportComponents[C Component](p *Pool, All func(context *PoolContext) []Component) []C {
|
||||||
|
components := p.All(All)
|
||||||
|
|
||||||
|
results := make([]C, 0, len(components))
|
||||||
|
for _, comp := range components {
|
||||||
|
if cc, ok := comp.(C); ok {
|
||||||
|
results = append(results, cc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
meta.Elem = tp.Elem()
|
return results
|
||||||
meta.Name = meta.Elem.PkgPath() + "." + meta.Elem.Name()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meta represents meta information about a component
|
// ExportComponent exports the first component that is a C from the pool.
|
||||||
type Meta struct {
|
//
|
||||||
Elem reflect.Type // the element type of the component
|
// All should be the function of the core that initializes all components.
|
||||||
Name string // the name of the component
|
// All should only make calls to [InitComponent].
|
||||||
}
|
func ExportComponent[C Component](p *Pool, All func(context *PoolContext) []Component) C {
|
||||||
|
components := p.All(All)
|
||||||
|
|
||||||
// New creates a new ComponentDescription
|
for _, comp := range components {
|
||||||
func (cd Meta) New() any {
|
if cc, ok := comp.(C); ok {
|
||||||
return reflect.New(cd.Elem).Interface()
|
return cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var c C
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
internal/component/pool_meta.go
Normal file
126
internal/component/pool_meta.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
package component
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/slicesx"
|
||||||
|
"github.com/tkw1536/goprogram/lib/reflectx"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// META
|
||||||
|
//
|
||||||
|
|
||||||
|
var metaCache sync.Map
|
||||||
|
|
||||||
|
// getMeta gets the component belonging to a component type
|
||||||
|
func getMeta[C Component]() meta {
|
||||||
|
tp := reflectx.TypeOf[C]()
|
||||||
|
|
||||||
|
// we already have a m => return it
|
||||||
|
if m, ok := metaCache.Load(tp); ok {
|
||||||
|
return m.(meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new m
|
||||||
|
var m meta
|
||||||
|
m.init(tp)
|
||||||
|
|
||||||
|
// store it in the cache
|
||||||
|
metaCache.Store(tp, m)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// meta stores meta-information about a specific component
|
||||||
|
type meta struct {
|
||||||
|
Name string // the type name of this component
|
||||||
|
Elem reflect.Type // the element type of the component
|
||||||
|
|
||||||
|
CFields map[string]reflect.Type // fields with type C for which C implements component
|
||||||
|
IFields map[string]reflect.Type // fields []I where I is an interface that implements component
|
||||||
|
}
|
||||||
|
|
||||||
|
// componentType is the type of components
|
||||||
|
var componentType = reflectx.TypeOf[Component]()
|
||||||
|
|
||||||
|
// init initializes this refklecttype
|
||||||
|
func (m *meta) init(tp reflect.Type) {
|
||||||
|
if tp.Kind() != reflect.Pointer && tp.Elem().Kind() != reflect.Struct {
|
||||||
|
panic("GetMeta: Type (" + tp.String() + ") must be backed by a pointer to slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Name = tp.Elem().PkgPath() + "." + tp.Elem().Name()
|
||||||
|
m.Elem = tp.Elem()
|
||||||
|
|
||||||
|
m.CFields = make(map[string]reflect.Type)
|
||||||
|
m.IFields = make(map[string]reflect.Type)
|
||||||
|
|
||||||
|
// fill the above variables, with a mapping of field name to struct
|
||||||
|
count := m.Elem.NumField()
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
field := m.Elem.Field(i)
|
||||||
|
|
||||||
|
name := field.Name
|
||||||
|
tp := field.Type
|
||||||
|
|
||||||
|
switch {
|
||||||
|
// field is a pointer to struct that implements a component
|
||||||
|
case tp.Implements(componentType) && tp.Kind() == reflect.Pointer && tp.Elem().Kind() == reflect.Struct:
|
||||||
|
m.CFields[name] = tp
|
||||||
|
|
||||||
|
// field is []I, where I is an interface that implements component
|
||||||
|
case tp.Kind() == reflect.Slice && tp.Elem().Kind() == reflect.Interface && tp.Elem().Implements(componentType):
|
||||||
|
m.IFields[name] = tp.Elem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new ComponentDescription
|
||||||
|
func (m meta) New() Component {
|
||||||
|
return reflect.New(m.Elem).Interface().(Component)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedsInitComponent
|
||||||
|
func (m meta) NeedsInitComponent() bool {
|
||||||
|
return len(m.CFields) > 0 || len(m.IFields) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitComponent sets up the fields of the given instance of a component.
|
||||||
|
func (m meta) InitComponent(instance reflect.Value, all []Component) {
|
||||||
|
elem := instance.Elem()
|
||||||
|
|
||||||
|
// assign the component fields
|
||||||
|
for field, eType := range m.CFields {
|
||||||
|
c := slicesx.First(all, func(c Component) bool {
|
||||||
|
return reflect.TypeOf(c).AssignableTo(eType)
|
||||||
|
})
|
||||||
|
|
||||||
|
field := elem.FieldByName(field)
|
||||||
|
field.Set(reflect.ValueOf(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign the multi subtypes
|
||||||
|
registryR := reflect.ValueOf(all)
|
||||||
|
for field, eType := range m.IFields {
|
||||||
|
cs := filterSubtype(registryR, eType)
|
||||||
|
field := elem.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
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package dis
|
package dis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/component"
|
||||||
|
|
@ -15,173 +13,53 @@ 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
|
|
||||||
// It is inlined into the [Distillery] struct, and initialized using [makeComponent].
|
|
||||||
//
|
|
||||||
// The caller is responsible for syncronizing access across multiple goroutines.
|
|
||||||
type components struct {
|
|
||||||
t int32 // t is the previously used thread id!
|
|
||||||
pool component.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// register returns all components of the distillery
|
// register returns all components of the distillery
|
||||||
func register(dis *Distillery, thread int32) []component.Component {
|
func (dis *Distillery) register(context *component.PoolContext) []component.Component {
|
||||||
return []component.Component{
|
return []component.Component{
|
||||||
ra[*web.Web](dis, thread),
|
ra[*web.Web](dis, context),
|
||||||
|
|
||||||
ra[*ssh.SSH](dis, thread),
|
ra[*ssh.SSH](dis, context),
|
||||||
|
|
||||||
r(dis, thread, func(ts *triplestore.Triplestore) {
|
r(dis, context, func(ts *triplestore.Triplestore) {
|
||||||
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
ts.BaseURL = "http://" + dis.Upstream.Triplestore
|
||||||
ts.PollContext = dis.Context()
|
ts.PollContext = dis.Context()
|
||||||
ts.PollInterval = time.Second
|
ts.PollInterval = time.Second
|
||||||
}),
|
}),
|
||||||
r(dis, thread, func(sql *sql.SQL) {
|
r(dis, context, func(sql *sql.SQL) {
|
||||||
sql.ServerURL = dis.Upstream.SQL
|
sql.ServerURL = dis.Upstream.SQL
|
||||||
sql.PollContext = dis.Context()
|
sql.PollContext = dis.Context()
|
||||||
sql.PollInterval = time.Second
|
sql.PollInterval = time.Second
|
||||||
}),
|
}),
|
||||||
|
|
||||||
ra[*instances.Instances](dis, thread),
|
ra[*instances.Instances](dis, context),
|
||||||
|
|
||||||
// Snapshots
|
// Snapshots
|
||||||
ra[*snapshots.Manager](dis, thread),
|
ra[*snapshots.Manager](dis, context),
|
||||||
ra[*snapshots.Config](dis, thread),
|
ra[*snapshots.Config](dis, context),
|
||||||
ra[*snapshots.Bookkeeping](dis, thread),
|
ra[*snapshots.Bookkeeping](dis, context),
|
||||||
ra[*snapshots.Filesystem](dis, thread),
|
ra[*snapshots.Filesystem](dis, context),
|
||||||
ra[*snapshots.Pathbuilders](dis, thread),
|
ra[*snapshots.Pathbuilders](dis, context),
|
||||||
|
|
||||||
// Control server
|
// Control server
|
||||||
r(dis, thread, func(control *control.Control) {
|
r(dis, context, func(control *control.Control) {
|
||||||
control.ResolverFile = core.PrefixConfig
|
control.ResolverFile = core.PrefixConfig
|
||||||
}),
|
}),
|
||||||
ra[*control.SelfHandler](dis, thread),
|
ra[*control.SelfHandler](dis, context),
|
||||||
r(dis, thread, func(resolver *resolver.Resolver) {
|
r(dis, context, func(resolver *resolver.Resolver) {
|
||||||
resolver.ResolverFile = core.PrefixConfig
|
resolver.ResolverFile = core.PrefixConfig
|
||||||
}),
|
}),
|
||||||
ra[*control.Info](dis, thread),
|
ra[*control.Info](dis, context),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// r initializes a component from the provided distillery.
|
// r initializes a component from the provided distillery.
|
||||||
func r[C component.Component](dis *Distillery, thread int32, init func(component C)) C {
|
func r[C component.Component](dis *Distillery, context *component.PoolContext, init func(component C)) C {
|
||||||
return component.InitComponent(&dis.pool, thread, dis.Core, makeInitFunction(dis, init))
|
return component.Make(context, dis.Core, init)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ra is like r, but does not provided additional initialization
|
// ra is like r, but does not provided additional initialization
|
||||||
func ra[C component.Component](dis *Distillery, thread int32) C {
|
func ra[C component.Component](dis *Distillery, context *component.PoolContext) C {
|
||||||
return r[C](dis, thread, nil)
|
return r[C](dis, context, nil)
|
||||||
}
|
|
||||||
|
|
||||||
var componentType = reflectx.TypeOf[component.Component]()
|
|
||||||
|
|
||||||
// makeInitFunction generate an init function for a specific component.
|
|
||||||
// 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]()
|
|
||||||
|
|
||||||
// this function automatically initializes component.Component-like fields of the instance.
|
|
||||||
// for this we first store two types of fields:
|
|
||||||
|
|
||||||
singles := make(map[string]reflect.Type) // fields of type C where C is a component.Type
|
|
||||||
multis := make(map[string]reflect.Type) // fields of type []C where C is an interface that implements component.
|
|
||||||
|
|
||||||
// fill the above variables, with a mapping of field name to struct
|
|
||||||
count := meta.Elem.NumField()
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
field := meta.Elem.Field(i)
|
|
||||||
|
|
||||||
name := field.Name
|
|
||||||
tp := field.Type
|
|
||||||
|
|
||||||
switch {
|
|
||||||
// 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ type Distillery struct {
|
||||||
// But for now this will just hold upstream configuration.
|
// But for now this will just hold upstream configuration.
|
||||||
Upstream Upstream
|
Upstream Upstream
|
||||||
|
|
||||||
// components hold references to the various components of the distillery.
|
// Pool holds all the components in this pool
|
||||||
components
|
pool component.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upstream contains the configuration for accessing remote configuration.
|
// Upstream contains the configuration for accessing remote configuration.
|
||||||
|
|
@ -47,6 +47,21 @@ func (dis *Distillery) Context() context.Context {
|
||||||
// PUBLIC COMPONENT GETTERS
|
// PUBLIC COMPONENT GETTERS
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// e is a convenience function to export a single component
|
||||||
|
func e[C component.Component](dis *Distillery) C {
|
||||||
|
return component.ExportComponent[C](
|
||||||
|
&dis.pool,
|
||||||
|
dis.register,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ea[C component.Component](dis *Distillery) []C {
|
||||||
|
return component.ExportComponents[C](
|
||||||
|
&dis.pool,
|
||||||
|
dis.register,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (dis *Distillery) Control() *control.Control {
|
func (dis *Distillery) Control() *control.Control {
|
||||||
return e[*control.Control](dis)
|
return e[*control.Control](dis)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package rlock
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RLock is like [sync.Mutex], but permits recursive locking.
|
|
||||||
type RLock struct {
|
|
||||||
m sync.Mutex // m is held internally
|
|
||||||
|
|
||||||
held bool
|
|
||||||
holder int
|
|
||||||
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()
|
|
||||||
switch {
|
|
||||||
case !rm.held:
|
|
||||||
rm.held = true
|
|
||||||
rm.holder = id
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue