Skip to content

Commit 5b3fa6d

Browse files
committed
Graceful: use GetManager instead of global
1 parent 7f0d7c0 commit 5b3fa6d

File tree

14 files changed

+195
-75
lines changed

14 files changed

+195
-75
lines changed

cmd/web.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package cmd
66

77
import (
8+
"context"
89
"fmt"
910
"net/http"
1011
_ "net/http/pprof" // Used for debugging if enabled and a web server is running
@@ -96,6 +97,10 @@ func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
9697
}
9798

9899
func runWeb(ctx *cli.Context) error {
100+
managerCtx, cancel := context.WithCancel(context.Background())
101+
graceful.InitManager(managerCtx)
102+
defer cancel()
103+
99104
if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
100105
log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
101106
} else {
@@ -195,8 +200,7 @@ func runWeb(ctx *cli.Context) error {
195200
log.Critical("Failed to start server: %v", err)
196201
}
197202
log.Info("HTTP Listener: %s Closed", listenAddr)
198-
graceful.Manager.WaitForServers()
199-
graceful.Manager.WaitForTerminate()
203+
<-graceful.GetManager().Done()
200204
log.Info("PID: %d Gitea Web Finished", os.Getpid())
201205
log.Close()
202206
return nil

cmd/web_graceful.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ func runHTTPSWithTLSConfig(network, listenAddr string, tlsConfig *tls.Config, m
2828

2929
// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
3030
func NoHTTPRedirector() {
31-
graceful.Manager.InformCleanup()
31+
graceful.GetManager().InformCleanup()
3232
}
3333

3434
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
3535
// for our main HTTP/HTTPS service
3636
func NoMainListener() {
37-
graceful.Manager.InformCleanup()
37+
graceful.GetManager().InformCleanup()
3838
}
3939

4040
func runFCGI(listenAddr string, m http.Handler) error {

integrations/integration_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package integrations
66

77
import (
88
"bytes"
9+
"context"
910
"database/sql"
1011
"encoding/json"
1112
"fmt"
@@ -24,6 +25,7 @@ import (
2425

2526
"code.gitea.io/gitea/models"
2627
"code.gitea.io/gitea/modules/base"
28+
"code.gitea.io/gitea/modules/graceful"
2729
"code.gitea.io/gitea/modules/setting"
2830
"code.gitea.io/gitea/routers"
2931
"code.gitea.io/gitea/routers/routes"
@@ -55,6 +57,10 @@ func NewNilResponseRecorder() *NilResponseRecorder {
5557
}
5658

5759
func TestMain(m *testing.M) {
60+
managerCtx, cancel := context.WithCancel(context.Background())
61+
graceful.InitManager(managerCtx)
62+
defer cancel()
63+
5864
initIntegrationTest()
5965
mac = routes.NewMacaron()
6066
routes.RegisterRoutes(mac)

modules/graceful/context.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (ctx *ChannelContext) Value(key interface{}) interface{} {
6262
// ShutdownContext returns a context.Context that is Done at shutdown
6363
// Callers using this context should ensure that they are registered as a running server
6464
// in order that they are waited for.
65-
func (g *gracefulManager) ShutdownContext() context.Context {
65+
func (g *Manager) ShutdownContext() context.Context {
6666
return &ChannelContext{
6767
done: g.IsShutdown(),
6868
err: ErrShutdown,
@@ -72,7 +72,7 @@ func (g *gracefulManager) ShutdownContext() context.Context {
7272
// HammerContext returns a context.Context that is Done at hammer
7373
// Callers using this context should ensure that they are registered as a running server
7474
// in order that they are waited for.
75-
func (g *gracefulManager) HammerContext() context.Context {
75+
func (g *Manager) HammerContext() context.Context {
7676
return &ChannelContext{
7777
done: g.IsHammer(),
7878
err: ErrHammer,
@@ -82,7 +82,7 @@ func (g *gracefulManager) HammerContext() context.Context {
8282
// TerminateContext returns a context.Context that is Done at terminate
8383
// Callers using this context should ensure that they are registered as a terminating server
8484
// in order that they are waited for.
85-
func (g *gracefulManager) TerminateContext() context.Context {
85+
func (g *Manager) TerminateContext() context.Context {
8686
return &ChannelContext{
8787
done: g.IsTerminate(),
8888
err: ErrTerminate,

modules/graceful/manager.go

Lines changed: 98 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package graceful
66

77
import (
88
"context"
9+
"sync"
910
"time"
1011

1112
"code.gitea.io/gitea/modules/git"
@@ -34,14 +35,27 @@ const (
3435
const numberOfServersToCreate = 3
3536

3637
// 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+
})
4559
}
4660

4761
// 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,
6175
// Please note that use of the atShutdown and atTerminate callbacks will create go-routines that will wait till their respective signals
6276
// - users must therefore be careful to only call these as necessary.
6377
// 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) {
6579
g.runningServerWaitGroup.Add(1)
6680
defer g.runningServerWaitGroup.Done()
6781
run(func(ctx context.Context, atShutdown func()) {
@@ -90,7 +104,7 @@ type RunnableWithShutdownChan func(atShutdown <-chan struct{}, atTerminate Callb
90104
// (Optionally IsHammer may be waited for instead however, this should be avoided if possible.)
91105
// The callback function provided to atTerminate must return once termination is complete.
92106
// 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) {
94108
g.runningServerWaitGroup.Add(1)
95109
defer g.runningServerWaitGroup.Done()
96110
run(g.IsShutdown(), func(ctx context.Context, atTerminate func()) {
@@ -101,14 +115,14 @@ func (g *gracefulManager) RunWithShutdownChan(run RunnableWithShutdownChan) {
101115
// RunWithShutdownContext takes a function that has a context to watch for shutdown.
102116
// After the provided context is Done(), the main function must return once shutdown is complete.
103117
// (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)) {
105119
g.runningServerWaitGroup.Add(1)
106120
defer g.runningServerWaitGroup.Done()
107121
run(g.ShutdownContext())
108122
}
109123

110124
// 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()) {
112126
g.terminateWaitGroup.Add(1)
113127
go func() {
114128
select {
@@ -121,7 +135,7 @@ func (g *gracefulManager) RunAtTerminate(ctx context.Context, terminate func())
121135
}
122136

123137
// 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()) {
125139
go func() {
126140
select {
127141
case <-g.IsShutdown():
@@ -132,7 +146,7 @@ func (g *gracefulManager) RunAtShutdown(ctx context.Context, shutdown func()) {
132146
}
133147

134148
// 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()) {
136150
go func() {
137151
select {
138152
case <-g.IsHammer():
@@ -141,11 +155,14 @@ func (g *gracefulManager) RunAtHammer(ctx context.Context, hammer func()) {
141155
}
142156
}()
143157
}
144-
func (g *gracefulManager) doShutdown() {
158+
func (g *Manager) doShutdown() {
145159
if !g.setStateTransition(stateRunning, stateShuttingDown) {
146160
return
147161
}
148162
g.lock.Lock()
163+
if g.shutdown == nil {
164+
g.shutdown = make(chan struct{})
165+
}
149166
close(g.shutdown)
150167
g.lock.Unlock()
151168

@@ -158,37 +175,54 @@ func (g *gracefulManager) doShutdown() {
158175
g.doHammerTime(0)
159176
<-time.After(1 * time.Second)
160177
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()
161185
}()
162186
}
163187

164-
func (g *gracefulManager) doHammerTime(d time.Duration) {
188+
func (g *Manager) doHammerTime(d time.Duration) {
165189
time.Sleep(d)
190+
g.lock.Lock()
191+
if g.hammer == nil {
192+
g.hammer = make(chan struct{})
193+
}
194+
g.lock.Unlock()
166195
select {
167196
case <-g.hammer:
168197
default:
169198
log.Warn("Setting Hammer condition")
199+
g.lock.Lock()
170200
close(g.hammer)
201+
g.lock.Unlock()
171202
}
172203

173204
}
174205

175-
func (g *gracefulManager) doTerminate() {
206+
func (g *Manager) doTerminate() {
176207
if !g.setStateTransition(stateShuttingDown, stateTerminate) {
177208
return
178209
}
179210
g.lock.Lock()
211+
if g.terminate == nil {
212+
g.terminate = make(chan struct{})
213+
}
180214
close(g.terminate)
181215
g.lock.Unlock()
182216
}
183217

184218
// 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 {
186220
return g.isChild
187221
}
188222

189223
// IsShutdown returns a channel which will be closed at shutdown.
190224
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
191-
func (g *gracefulManager) IsShutdown() <-chan struct{} {
225+
func (g *Manager) IsShutdown() <-chan struct{} {
192226
g.lock.RLock()
193227
if g.shutdown == nil {
194228
g.lock.RUnlock()
@@ -207,7 +241,7 @@ func (g *gracefulManager) IsShutdown() <-chan struct{} {
207241
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
208242
// Servers running within the running server wait group should respond to IsHammer
209243
// if not shutdown already
210-
func (g *gracefulManager) IsHammer() <-chan struct{} {
244+
func (g *Manager) IsHammer() <-chan struct{} {
211245
g.lock.RLock()
212246
if g.hammer == nil {
213247
g.lock.RUnlock()
@@ -225,7 +259,7 @@ func (g *gracefulManager) IsHammer() <-chan struct{} {
225259
// IsTerminate returns a channel which will be closed at terminate
226260
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
227261
// IsTerminate will only close once all running servers have stopped
228-
func (g *gracefulManager) IsTerminate() <-chan struct{} {
262+
func (g *Manager) IsTerminate() <-chan struct{} {
229263
g.lock.RLock()
230264
if g.terminate == nil {
231265
g.lock.RUnlock()
@@ -243,29 +277,29 @@ func (g *gracefulManager) IsTerminate() <-chan struct{} {
243277
// ServerDone declares a running server done and subtracts one from the
244278
// running server wait group. Users probably do not want to call this
245279
// and should use one of the RunWithShutdown* functions
246-
func (g *gracefulManager) ServerDone() {
280+
func (g *Manager) ServerDone() {
247281
g.runningServerWaitGroup.Done()
248282
}
249283

250284
// WaitForServers waits for all running servers to finish. Users should probably
251285
// instead use AtTerminate or IsTerminate
252-
func (g *gracefulManager) WaitForServers() {
286+
func (g *Manager) WaitForServers() {
253287
g.runningServerWaitGroup.Wait()
254288
}
255289

256290
// WaitForTerminate waits for all terminating actions to finish.
257291
// Only the main go-routine should use this
258-
func (g *gracefulManager) WaitForTerminate() {
292+
func (g *Manager) WaitForTerminate() {
259293
g.terminateWaitGroup.Wait()
260294
}
261295

262-
func (g *gracefulManager) getState() state {
296+
func (g *Manager) getState() state {
263297
g.lock.RLock()
264298
defer g.lock.RUnlock()
265299
return g.state
266300
}
267301

268-
func (g *gracefulManager) setStateTransition(old, new state) bool {
302+
func (g *Manager) setStateTransition(old, new state) bool {
269303
if old != g.getState() {
270304
return false
271305
}
@@ -279,7 +313,7 @@ func (g *gracefulManager) setStateTransition(old, new state) bool {
279313
return true
280314
}
281315

282-
func (g *gracefulManager) setState(st state) {
316+
func (g *Manager) setState(st state) {
283317
g.lock.Lock()
284318
defer g.lock.Unlock()
285319

@@ -288,6 +322,42 @@ func (g *gracefulManager) setState(st state) {
288322

289323
// InformCleanup tells the cleanup wait group that we have either taken a listener
290324
// or will not be taking a listener
291-
func (g *gracefulManager) InformCleanup() {
325+
func (g *Manager) InformCleanup() {
292326
g.createServerWaitGroup.Done()
293327
}
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

Comments
 (0)