]>
Commit | Line | Data |
---|---|---|
1 | // Package run implements an actor-runner with deterministic teardown. It is | |
2 | // somewhat similar to package errgroup, except it does not require actor | |
3 | // goroutines to understand context semantics. This makes it suitable for use in | |
4 | // more circumstances; for example, goroutines which are handling connections | |
5 | // from net.Listeners, or scanning input from a closable io.Reader. | |
6 | package run | |
7 | ||
8 | // Group collects actors (functions) and runs them concurrently. | |
9 | // When one actor (function) returns, all actors are interrupted. | |
10 | // The zero value of a Group is useful. | |
11 | type Group struct { | |
12 | actors []actor | |
13 | } | |
14 | ||
15 | // Add an actor (function) to the group. Each actor must be pre-emptable by an | |
16 | // interrupt function. That is, if interrupt is invoked, execute should return. | |
17 | // Also, it must be safe to call interrupt even after execute has returned. | |
18 | // | |
19 | // The first actor (function) to return interrupts all running actors. | |
20 | // The error is passed to the interrupt functions, and is returned by Run. | |
21 | func (g *Group) Add(execute func() error, interrupt func(error)) { | |
22 | g.actors = append(g.actors, actor{execute, interrupt}) | |
23 | } | |
24 | ||
25 | // Run all actors (functions) concurrently. | |
26 | // When the first actor returns, all others are interrupted. | |
27 | // Run only returns when all actors have exited. | |
28 | // Run returns the error returned by the first exiting actor. | |
29 | func (g *Group) Run() error { | |
30 | if len(g.actors) == 0 { | |
31 | return nil | |
32 | } | |
33 | ||
34 | // Run each actor. | |
35 | errors := make(chan error, len(g.actors)) | |
36 | for _, a := range g.actors { | |
37 | go func(a actor) { | |
38 | errors <- a.execute() | |
39 | }(a) | |
40 | } | |
41 | ||
42 | // Wait for the first actor to stop. | |
43 | err := <-errors | |
44 | ||
45 | // Signal all actors to stop. | |
46 | for _, a := range g.actors { | |
47 | a.interrupt(err) | |
48 | } | |
49 | ||
50 | // Wait for all actors to stop. | |
51 | for i := 1; i < cap(errors); i++ { | |
52 | <-errors | |
53 | } | |
54 | ||
55 | // Return the original error. | |
56 | return err | |
57 | } | |
58 | ||
59 | type actor struct { | |
60 | execute func() error | |
61 | interrupt func(error) | |
62 | } |