-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnserve.go
97 lines (90 loc) · 2.74 KB
/
nserve.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
// Package nserve helps with server startup and shutdown by by allowing libraries
// too register themselves with hooks that run at startup and shutdown.
package nserve
import (
"context"
"sync"
"github.com/muir/nject"
)
// App provides hooks to start and stop libraries that are used by an app. It
// expected that an App corresponds to a service and that libraries that the
// service uses need to be started & stopped.
type App struct {
lock sync.Mutex // held when adding hooks
runLock sync.Mutex // held when running hooks
Hooks map[hookId][]nject.Provider
}
// CreateApp will use nject.Run() to invoke the providers that make up the service
// represented by the app.
func CreateApp(name string, providers ...interface{}) (*App, error) {
app := &App{
Hooks: make(map[hookId][]nject.Provider),
}
ctx, cancel := context.WithCancel(context.Background())
app.Hooks[Shutdown.Id] = append(app.Hooks[Shutdown.Id], nject.Provide("app-cancel-ctx", cancel))
err := nject.Run(name, ctx, app, nject.Sequence("app-providers", providers...))
return app, err
}
// On registers a callback to be invoked on hook invocation. This can be used during
// callbacks, for example a start callback, can register a stop callback. Each call
// to On() adds one nject provider chain. By default, only the last function will
// be called and nject annotations can be used to control the behavior.
func (app *App) On(h *Hook, providers ...interface{}) {
app.lock.Lock()
defer app.lock.Unlock()
app.Hooks[h.Id] = append(app.Hooks[h.Id], nject.Sequence("on-"+h.Name, providers...))
}
// Do invokes the callbacks for a hook. It returns only the first error reported
// unless the hook provides an error combiner that preserves the other errors.
func (app *App) Do(h *Hook) error {
app.runLock.Lock()
defer app.runLock.Unlock()
return app.do(h)
}
func (app *App) do(h *Hook) error {
ec := h.ErrorCombiner
if ec == nil {
ec = func(err, _ error) error { return err }
}
ecw := func(e1, e2 error) error {
if e1 == nil {
return e2
}
if e2 == nil {
return e1
}
return ec(e1, e2)
}
app.lock.Lock()
chains := make([]nject.Provider, len(app.Hooks[h.Id]))
copy(chains, app.Hooks[h.Id])
app.lock.Unlock()
var err error
runChain := func(chain nject.Provider) {
e := nject.Run("hook-"+h.Name,
app,
nject.Sequence("hook-"+h.Name+"-providers", h.Providers...),
chain)
err = ecw(err, e)
}
if h.Order == ForwardOrder {
for _, chain := range chains {
runChain(chain)
if err != nil && !h.ContinuePast {
break
}
}
} else {
for i := len(chains) - 1; i >= 0; i-- {
chain := chains[i]
runChain(chain)
if err != nil && !h.ContinuePast {
break
}
}
}
for _, oe := range h.InvokeOnError {
err = ecw(err, app.do(oe))
}
return err
}