63 lines
1.8 KiB
Go
63 lines
1.8 KiB
Go
|
// Package run implements an actor-runner with deterministic teardown. It is
|
||
|
// somewhat similar to package errgroup, except it does not require actor
|
||
|
// goroutines to understand context semantics. This makes it suitable for use in
|
||
|
// more circumstances; for example, goroutines which are handling connections
|
||
|
// from net.Listeners, or scanning input from a closable io.Reader.
|
||
|
package run
|
||
|
|
||
|
// Group collects actors (functions) and runs them concurrently.
|
||
|
// When one actor (function) returns, all actors are interrupted.
|
||
|
// The zero value of a Group is useful.
|
||
|
type Group struct {
|
||
|
actors []actor
|
||
|
}
|
||
|
|
||
|
// Add an actor (function) to the group. Each actor must be pre-emptable by an
|
||
|
// interrupt function. That is, if interrupt is invoked, execute should return.
|
||
|
// Also, it must be safe to call interrupt even after execute has returned.
|
||
|
//
|
||
|
// The first actor (function) to return interrupts all running actors.
|
||
|
// The error is passed to the interrupt functions, and is returned by Run.
|
||
|
func (g *Group) Add(execute func() error, interrupt func(error)) {
|
||
|
g.actors = append(g.actors, actor{execute, interrupt})
|
||
|
}
|
||
|
|
||
|
// Run all actors (functions) concurrently.
|
||
|
// When the first actor returns, all others are interrupted.
|
||
|
// Run only returns when all actors have exited.
|
||
|
// Run returns the error returned by the first exiting actor.
|
||
|
func (g *Group) Run() error {
|
||
|
if len(g.actors) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Run each actor.
|
||
|
errors := make(chan error, len(g.actors))
|
||
|
for _, a := range g.actors {
|
||
|
go func(a actor) {
|
||
|
errors <- a.execute()
|
||
|
}(a)
|
||
|
}
|
||
|
|
||
|
// Wait for the first actor to stop.
|
||
|
err := <-errors
|
||
|
|
||
|
// Signal all actors to stop.
|
||
|
for _, a := range g.actors {
|
||
|
a.interrupt(err)
|
||
|
}
|
||
|
|
||
|
// Wait for all actors to stop.
|
||
|
for i := 1; i < cap(errors); i++ {
|
||
|
<-errors
|
||
|
}
|
||
|
|
||
|
// Return the original error.
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
type actor struct {
|
||
|
execute func() error
|
||
|
interrupt func(error)
|
||
|
}
|