Initial commit
This commit is contained in:
+178
@@ -0,0 +1,178 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user