179 lines
4.7 KiB
Go
179 lines
4.7 KiB
Go
package camfish
|
|
|
|
import "iter"
|
|
import "slices"
|
|
import "context"
|
|
|
|
// actorSets stores a sorted set of all actors, as well as information about
|
|
// their state.
|
|
type actorSets struct {
|
|
all actorSet[Actor]
|
|
inf map[Actor] actorInfo
|
|
nextOrder int
|
|
|
|
flagAdder actorSet[FlagAdder]
|
|
configProcessor actorSet[ConfigProcessor]
|
|
configurable actorSet[Configurable]
|
|
initializable actorSet[Initializable]
|
|
runnable actorSet[Runnable]
|
|
trimmable actorSet[Trimmable]
|
|
}
|
|
|
|
// actorInfo holds information about a running actor.
|
|
type actorInfo struct {
|
|
// ctx is the context of the actor. It is passed to the actor's Run
|
|
// method.
|
|
ctx context.Context
|
|
// done is used to stop the actor. It is created when the actor is added
|
|
// to the actorSets.
|
|
done func()
|
|
// stopped is closed once the actor's run method exits. If the actor
|
|
// does not have a run method, it will be nil.
|
|
stopped chan struct { }
|
|
// order is the semantic order of the actor within the sets. This is
|
|
// incremented automatically for each actor that is added. It is never
|
|
// less than one.
|
|
order int
|
|
}
|
|
|
|
// actorSetIface holds only the add/del/clear methods of actorSet.
|
|
type actorSetIface interface {
|
|
add(actor Actor)
|
|
del(actor Actor)
|
|
clear()
|
|
}
|
|
|
|
// All returns an iterator over all sets in the collection.
|
|
func (sets *actorSets) All() iter.Seq[actorSetIface] {
|
|
return func(yield func(actorSetIface) bool) {
|
|
yield(&sets.all)
|
|
yield(&sets.flagAdder)
|
|
yield(&sets.configProcessor)
|
|
yield(&sets.configurable)
|
|
yield(&sets.initializable)
|
|
yield(&sets.runnable)
|
|
yield(&sets.trimmable)
|
|
}
|
|
}
|
|
|
|
// add adds an actor under the given parent context. This is a write operation.
|
|
func (this *actorSets) add(ctx context.Context, actor Actor) {
|
|
if this.inf == nil { this.inf = make(map[Actor] actorInfo)}
|
|
actorCtx, done := context.WithCancel(ctx)
|
|
this.nextOrder ++
|
|
info := actorInfo {
|
|
ctx: actorCtx,
|
|
done: done,
|
|
order: this.nextOrder,
|
|
}
|
|
if _, ok := actor.(Runnable); ok {
|
|
info.stopped = make(chan struct { })
|
|
}
|
|
this.inf[actor] = info
|
|
for set := range this.All() {
|
|
set.add(actor)
|
|
}
|
|
}
|
|
|
|
// del removes an actor. This is a write operation.
|
|
func (this *actorSets) del(actor Actor) {
|
|
delete(this.inf, actor)
|
|
for set := range this.All() {
|
|
set.del(actor)
|
|
}
|
|
}
|
|
|
|
// clear removes all actors.
|
|
func (this *actorSets) clear() {
|
|
clear(this.inf)
|
|
for set := range this.All() {
|
|
set.clear()
|
|
}
|
|
}
|
|
|
|
// info gets information about an actor.
|
|
func (this *actorSets) info(actor Actor) actorInfo {
|
|
if this.inf == nil { return actorInfo { } }
|
|
return this.inf[actor]
|
|
}
|
|
|
|
// sortActors sorts actors according to the order in which they were added.
|
|
func sortActors[T comparable] (sets *actorSets, actors []T) []T {
|
|
slices.SortFunc(actors, func (left, right T) int {
|
|
// :3
|
|
leftOrder := sets.info(any(left).(Actor)).order
|
|
rightOrder := sets.info(any(right).(Actor)).order
|
|
if leftOrder < 1 || rightOrder < 1 {
|
|
panic("could not sort actors, some were invalid")
|
|
}
|
|
switch {
|
|
case leftOrder < rightOrder: return -1
|
|
case rightOrder > leftOrder: return 1
|
|
default: return 0
|
|
}
|
|
})
|
|
return actors
|
|
}
|
|
|
|
// actorSet is a list that holds all actors of a type. it must not return any
|
|
// references to data that is referenced elsewehre, except for references to
|
|
// actors.
|
|
type actorSet[T comparable] struct {
|
|
actors map[T] string
|
|
}
|
|
|
|
// add adds an actor, if it is of the set's type
|
|
func (this *actorSet[T]) add(actor Actor) {
|
|
if this.actors == nil { this.actors = make(map[T] string) }
|
|
if item, ok := actor.(T); ok {
|
|
this.actors[item] = actor.Type()
|
|
}
|
|
}
|
|
|
|
// add removes an actor, if it is of the set's type
|
|
func (this *actorSet[T]) del(actor Actor) {
|
|
if this.actors == nil { return }
|
|
if item, ok := actor.(T); ok {
|
|
delete(this.actors, item)
|
|
}
|
|
}
|
|
|
|
// clear removes all actors.
|
|
func (this *actorSet[T]) clear() {
|
|
if this.actors == nil { return }
|
|
clear(this.actors)
|
|
}
|
|
|
|
// find returns the first actor with the given type.
|
|
func (this *actorSet[T]) find(typ string) T {
|
|
for actor, actorType := range this.actors {
|
|
if actorType == typ { return actor }
|
|
}
|
|
var zero T
|
|
return zero
|
|
}
|
|
|
|
// findAll returns all actors with the given type. This method allocates and
|
|
// returns a slice instead of an iterator for concurrency reasons.
|
|
func (this *actorSet[T]) findAll(typ string) []T {
|
|
if this.actors == nil { return nil }
|
|
slice := []T { }
|
|
for actor, actorType := range this.actors {
|
|
if actorType == typ {
|
|
slice = append(slice, actor)
|
|
}
|
|
}
|
|
return slice
|
|
}
|
|
|
|
// all returns all actors. This method allocates and returns a slice instead of
|
|
// an iterator for concurrency reasons.
|
|
func (this *actorSet[T]) all() []T {
|
|
if this.actors == nil { return nil }
|
|
slice := []T { }
|
|
for actor := range this.actors {
|
|
slice = append(slice, actor)
|
|
}
|
|
return slice
|
|
}
|