@@ -6,6 +6,7 @@ package graceful
6
6
7
7
import (
8
8
"context"
9
+ "sync"
9
10
"time"
10
11
11
12
"code.gitea.io/gitea/modules/git"
@@ -34,14 +35,27 @@ const (
34
35
const numberOfServersToCreate = 3
35
36
36
37
// Manager represents the graceful server manager interface
37
- var Manager * gracefulManager
38
-
39
- func init () {
40
- Manager = newGracefulManager (context .Background ())
41
- // Set the git default context to the HammerContext
42
- git .DefaultContext = Manager .HammerContext ()
43
- // Set the process default context to the HammerContext
44
- process .DefaultContext = Manager .HammerContext ()
38
+ var manager * Manager
39
+
40
+ var initOnce = sync.Once {}
41
+
42
+ // GetManager returns the Manager
43
+ func GetManager () * Manager {
44
+ InitManager (context .Background ())
45
+ return manager
46
+ }
47
+
48
+ // InitManager creates the graceful manager in the provided context
49
+ func InitManager (ctx context.Context ) {
50
+ initOnce .Do (func () {
51
+ manager = newGracefulManager (ctx )
52
+
53
+ // Set the git default context to the HammerContext
54
+ git .DefaultContext = manager .HammerContext ()
55
+
56
+ // Set the process default context to the HammerContext
57
+ process .DefaultContext = manager .HammerContext ()
58
+ })
45
59
}
46
60
47
61
// CallbackWithContext is combined runnable and context to watch to see if the caller has finished
@@ -61,7 +75,7 @@ type RunnableWithShutdownFns func(atShutdown, atTerminate func(context.Context,
61
75
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
62
76
// - users must therefore be careful to only call these as necessary.
63
77
// If run is not expected to run indefinitely RunWithShutdownChan is likely to be more appropriate.
64
- func (g * gracefulManager ) RunWithShutdownFns (run RunnableWithShutdownFns ) {
78
+ func (g * Manager ) RunWithShutdownFns (run RunnableWithShutdownFns ) {
65
79
g .runningServerWaitGroup .Add (1 )
66
80
defer g .runningServerWaitGroup .Done ()
67
81
run (func (ctx context.Context , atShutdown func ()) {
@@ -90,7 +104,7 @@ type RunnableWithShutdownChan func(atShutdown <-chan struct{}, atTerminate Callb
90
104
// (Optionally IsHammer may be waited for instead however, this should be avoided if possible.)
91
105
// The callback function provided to atTerminate must return once termination is complete.
92
106
// Please note that use of the atTerminate function will create a go-routine that will wait till terminate - users must therefore be careful to only call this as necessary.
93
- func (g * gracefulManager ) RunWithShutdownChan (run RunnableWithShutdownChan ) {
107
+ func (g * Manager ) RunWithShutdownChan (run RunnableWithShutdownChan ) {
94
108
g .runningServerWaitGroup .Add (1 )
95
109
defer g .runningServerWaitGroup .Done ()
96
110
run (g .IsShutdown (), func (ctx context.Context , atTerminate func ()) {
@@ -101,14 +115,14 @@ func (g *gracefulManager) RunWithShutdownChan(run RunnableWithShutdownChan) {
101
115
// RunWithShutdownContext takes a function that has a context to watch for shutdown.
102
116
// After the provided context is Done(), the main function must return once shutdown is complete.
103
117
// (Optionally the HammerContext may be obtained and waited for however, this should be avoided if possible.)
104
- func (g * gracefulManager ) RunWithShutdownContext (run func (context.Context )) {
118
+ func (g * Manager ) RunWithShutdownContext (run func (context.Context )) {
105
119
g .runningServerWaitGroup .Add (1 )
106
120
defer g .runningServerWaitGroup .Done ()
107
121
run (g .ShutdownContext ())
108
122
}
109
123
110
124
// RunAtTerminate adds to the terminate wait group and creates a go-routine to run the provided function at termination
111
- func (g * gracefulManager ) RunAtTerminate (ctx context.Context , terminate func ()) {
125
+ func (g * Manager ) RunAtTerminate (ctx context.Context , terminate func ()) {
112
126
g .terminateWaitGroup .Add (1 )
113
127
go func () {
114
128
select {
@@ -121,7 +135,7 @@ func (g *gracefulManager) RunAtTerminate(ctx context.Context, terminate func())
121
135
}
122
136
123
137
// RunAtShutdown creates a go-routine to run the provided function at shutdown
124
- func (g * gracefulManager ) RunAtShutdown (ctx context.Context , shutdown func ()) {
138
+ func (g * Manager ) RunAtShutdown (ctx context.Context , shutdown func ()) {
125
139
go func () {
126
140
select {
127
141
case <- g .IsShutdown ():
@@ -132,7 +146,7 @@ func (g *gracefulManager) RunAtShutdown(ctx context.Context, shutdown func()) {
132
146
}
133
147
134
148
// RunAtHammer creates a go-routine to run the provided function at shutdown
135
- func (g * gracefulManager ) RunAtHammer (ctx context.Context , hammer func ()) {
149
+ func (g * Manager ) RunAtHammer (ctx context.Context , hammer func ()) {
136
150
go func () {
137
151
select {
138
152
case <- g .IsHammer ():
@@ -141,11 +155,14 @@ func (g *gracefulManager) RunAtHammer(ctx context.Context, hammer func()) {
141
155
}
142
156
}()
143
157
}
144
- func (g * gracefulManager ) doShutdown () {
158
+ func (g * Manager ) doShutdown () {
145
159
if ! g .setStateTransition (stateRunning , stateShuttingDown ) {
146
160
return
147
161
}
148
162
g .lock .Lock ()
163
+ if g .shutdown == nil {
164
+ g .shutdown = make (chan struct {})
165
+ }
149
166
close (g .shutdown )
150
167
g .lock .Unlock ()
151
168
@@ -158,37 +175,54 @@ func (g *gracefulManager) doShutdown() {
158
175
g .doHammerTime (0 )
159
176
<- time .After (1 * time .Second )
160
177
g .doTerminate ()
178
+ g .WaitForTerminate ()
179
+ g .lock .Lock ()
180
+ if g .done == nil {
181
+ g .done = make (chan struct {})
182
+ }
183
+ close (g .done )
184
+ g .lock .Unlock ()
161
185
}()
162
186
}
163
187
164
- func (g * gracefulManager ) doHammerTime (d time.Duration ) {
188
+ func (g * Manager ) doHammerTime (d time.Duration ) {
165
189
time .Sleep (d )
190
+ g .lock .Lock ()
191
+ if g .hammer == nil {
192
+ g .hammer = make (chan struct {})
193
+ }
194
+ g .lock .Unlock ()
166
195
select {
167
196
case <- g .hammer :
168
197
default :
169
198
log .Warn ("Setting Hammer condition" )
199
+ g .lock .Lock ()
170
200
close (g .hammer )
201
+ g .lock .Unlock ()
171
202
}
172
203
173
204
}
174
205
175
- func (g * gracefulManager ) doTerminate () {
206
+ func (g * Manager ) doTerminate () {
176
207
if ! g .setStateTransition (stateShuttingDown , stateTerminate ) {
177
208
return
178
209
}
179
210
g .lock .Lock ()
211
+ if g .terminate == nil {
212
+ g .terminate = make (chan struct {})
213
+ }
180
214
close (g .terminate )
181
215
g .lock .Unlock ()
182
216
}
183
217
184
218
// IsChild returns if the current process is a child of previous Gitea process
185
- func (g * gracefulManager ) IsChild () bool {
219
+ func (g * Manager ) IsChild () bool {
186
220
return g .isChild
187
221
}
188
222
189
223
// IsShutdown returns a channel which will be closed at shutdown.
190
224
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
191
- func (g * gracefulManager ) IsShutdown () <- chan struct {} {
225
+ func (g * Manager ) IsShutdown () <- chan struct {} {
192
226
g .lock .RLock ()
193
227
if g .shutdown == nil {
194
228
g .lock .RUnlock ()
@@ -207,7 +241,7 @@ func (g *gracefulManager) IsShutdown() <-chan struct{} {
207
241
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
208
242
// Servers running within the running server wait group should respond to IsHammer
209
243
// if not shutdown already
210
- func (g * gracefulManager ) IsHammer () <- chan struct {} {
244
+ func (g * Manager ) IsHammer () <- chan struct {} {
211
245
g .lock .RLock ()
212
246
if g .hammer == nil {
213
247
g .lock .RUnlock ()
@@ -225,7 +259,7 @@ func (g *gracefulManager) IsHammer() <-chan struct{} {
225
259
// IsTerminate returns a channel which will be closed at terminate
226
260
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
227
261
// IsTerminate will only close once all running servers have stopped
228
- func (g * gracefulManager ) IsTerminate () <- chan struct {} {
262
+ func (g * Manager ) IsTerminate () <- chan struct {} {
229
263
g .lock .RLock ()
230
264
if g .terminate == nil {
231
265
g .lock .RUnlock ()
@@ -243,29 +277,29 @@ func (g *gracefulManager) IsTerminate() <-chan struct{} {
243
277
// ServerDone declares a running server done and subtracts one from the
244
278
// running server wait group. Users probably do not want to call this
245
279
// and should use one of the RunWithShutdown* functions
246
- func (g * gracefulManager ) ServerDone () {
280
+ func (g * Manager ) ServerDone () {
247
281
g .runningServerWaitGroup .Done ()
248
282
}
249
283
250
284
// WaitForServers waits for all running servers to finish. Users should probably
251
285
// instead use AtTerminate or IsTerminate
252
- func (g * gracefulManager ) WaitForServers () {
286
+ func (g * Manager ) WaitForServers () {
253
287
g .runningServerWaitGroup .Wait ()
254
288
}
255
289
256
290
// WaitForTerminate waits for all terminating actions to finish.
257
291
// Only the main go-routine should use this
258
- func (g * gracefulManager ) WaitForTerminate () {
292
+ func (g * Manager ) WaitForTerminate () {
259
293
g .terminateWaitGroup .Wait ()
260
294
}
261
295
262
- func (g * gracefulManager ) getState () state {
296
+ func (g * Manager ) getState () state {
263
297
g .lock .RLock ()
264
298
defer g .lock .RUnlock ()
265
299
return g .state
266
300
}
267
301
268
- func (g * gracefulManager ) setStateTransition (old , new state ) bool {
302
+ func (g * Manager ) setStateTransition (old , new state ) bool {
269
303
if old != g .getState () {
270
304
return false
271
305
}
@@ -279,7 +313,7 @@ func (g *gracefulManager) setStateTransition(old, new state) bool {
279
313
return true
280
314
}
281
315
282
- func (g * gracefulManager ) setState (st state ) {
316
+ func (g * Manager ) setState (st state ) {
283
317
g .lock .Lock ()
284
318
defer g .lock .Unlock ()
285
319
@@ -288,6 +322,42 @@ func (g *gracefulManager) setState(st state) {
288
322
289
323
// InformCleanup tells the cleanup wait group that we have either taken a listener
290
324
// or will not be taking a listener
291
- func (g * gracefulManager ) InformCleanup () {
325
+ func (g * Manager ) InformCleanup () {
292
326
g .createServerWaitGroup .Done ()
293
327
}
328
+
329
+ // Done allows the manager to be viewed as a context.Context, it returns a channel that is closed when the server is finished terminating
330
+ func (g * Manager ) Done () <- chan struct {} {
331
+ g .lock .RLock ()
332
+ if g .done == nil {
333
+ g .lock .RUnlock ()
334
+ g .lock .Lock ()
335
+ if g .done == nil {
336
+ g .done = make (chan struct {})
337
+ }
338
+ defer g .lock .Unlock ()
339
+ return g .done
340
+ }
341
+ defer g .lock .RUnlock ()
342
+ return g .done
343
+ }
344
+
345
+ // Err allows the manager to be viewed as a context.Context done at Terminate, it returns ErrTerminate
346
+ func (g * Manager ) Err () error {
347
+ select {
348
+ case <- g .Done ():
349
+ return ErrTerminate
350
+ default :
351
+ return nil
352
+ }
353
+ }
354
+
355
+ // Value allows the manager to be viewed as a context.Context done at Terminate, it has no values
356
+ func (g * Manager ) Value (key interface {}) interface {} {
357
+ return nil
358
+ }
359
+
360
+ // Deadline returns nil as there is no fixed Deadline for the manager, it allows the manager to be viewed as a context.Context
361
+ func (g * Manager ) Deadline () (deadline time.Time , ok bool ) {
362
+ return
363
+ }
0 commit comments