Codebase list golang-github-zenazn-goji / 84fcf9f
graceful: add {Pre,Post}HookWithSignal functions Carl Jackson 5 years ago
1 changed file(s) with 44 addition(s) and 18 deletion(s). Raw diff Collapse all Expand all
1111
1212 var mu sync.Mutex // protects everything that follows
1313 var listeners = make([]*listener.T, 0)
14 var prehooks = make([]func(), 0)
15 var posthooks = make([]func(), 0)
14 var prehooks = make([]func(os.Signal), 0)
15 var posthooks = make([]func(os.Signal), 0)
1616 var closing int32
1717 var doubleKick, timeout time.Duration
1818
3939 signal.Stop(sigchan)
4040 }
4141
42 // PreHookWithSignal registers a function to be called before any of this
43 // package's normal shutdown actions, which recieves the signal that caused the
44 // shutdown (or nil for manual shutdowns). All listeners will be called in the
45 // order they were added, from a single goroutine.
46 func PreHookWithSignal(f func(os.Signal)) {
47 mu.Lock()
48 defer mu.Unlock()
49
50 prehooks = append(prehooks, f)
51 }
52
4253 // PreHook registers a function to be called before any of this package's normal
4354 // shutdown actions. All listeners will be called in the order they were added,
4455 // from a single goroutine.
4556 func PreHook(f func()) {
46 mu.Lock()
47 defer mu.Unlock()
48
49 prehooks = append(prehooks, f)
57 PreHookWithSignal(func(_ os.Signal) {
58 f()
59 })
60 }
61
62 // PostHookWithSignal registers a function to be called after all of this
63 // package's normal shutdown actions, which receives the signal that caused the
64 // shutdown (or nil for manual shutdowns). All listeners will be called in the
65 // order they were added, from a single goroutine, and are guaranteed to be
66 // called after all listening connections have been closed, but before Wait()
67 // returns.
68 //
69 // If you've Hijacked any connections that must be gracefully shut down in some
70 // other way (since this library disowns all hijacked connections), it's
71 // reasonable to use a PostHook to signal and wait for them.
72 func PostHookWithSignal(f func(os.Signal)) {
73 mu.Lock()
74 defer mu.Unlock()
75
76 posthooks = append(posthooks, f)
5077 }
5178
5279 // PostHook registers a function to be called after all of this package's normal
5885 // other way (since this library disowns all hijacked connections), it's
5986 // reasonable to use a PostHook to signal and wait for them.
6087 func PostHook(f func()) {
61 mu.Lock()
62 defer mu.Unlock()
63
64 posthooks = append(posthooks, f)
88 PostHookWithSignal(func(_ os.Signal) {
89 f()
90 })
6591 }
6692
6793 // Shutdown manually triggers a shutdown from your application. Like Wait,
6894 // blocks until all connections have gracefully shut down.
6995 func Shutdown() {
70 shutdown(false)
96 shutdown(false, nil)
7197 }
7298
7399 // ShutdownNow triggers an immediate shutdown from your application. All
74100 // connections (not just those that are idle) are immediately closed, even if
75101 // they are in the middle of serving a request.
76102 func ShutdownNow() {
77 shutdown(true)
103 shutdown(true, nil)
78104 }
79105
80106 // DoubleKickWindow sets the length of the window during which two back-to-back
122148 func sigLoop() {
123149 var last time.Time
124150 for {
125 <-sigchan
151 sig := <-sigchan
126152 now := time.Now()
127153 mu.Lock()
128154 force := doubleKick != 0 && now.Sub(last) < doubleKick
129155 if t := timeout; t != 0 && !force {
130156 go func() {
131157 time.Sleep(t)
132 shutdown(true)
158 shutdown(true, sig)
133159 }()
134160 }
135161 mu.Unlock()
136 go shutdown(force)
162 go shutdown(force, sig)
137163 last = now
138164 }
139165 }
140166
141167 var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once
142168
143 func shutdown(force bool) {
169 func shutdown(force bool, sig os.Signal) {
144170 preOnce.Do(func() {
145171 mu.Lock()
146172 defer mu.Unlock()
147173 for _, f := range prehooks {
148 f()
174 f(sig)
149175 }
150176 })
151177
163189 mu.Lock()
164190 defer mu.Unlock()
165191 for _, f := range posthooks {
166 f()
192 f(sig)
167193 }
168194 })
169195