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] runShutdownable actorSet[RunShutdownable] 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.runShutdownable) 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, } _, isRunnable := actor.(Runnable) _, isRunShutdownable := actor.(RunShutdownable) if isRunnable || isRunShutdownable { 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 }