code.gitea.io/gitea@v1.22.3/modules/graceful/manager_common.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package graceful 5 6 import ( 7 "context" 8 "runtime/pprof" 9 "sync" 10 "time" 11 ) 12 13 // FIXME: it seems that there is a bug when using systemd Type=notify: the "Install Page" (INSTALL_LOCK=false) doesn't notify properly. 14 // At the moment, no idea whether it also affects Windows Service, or whether it's a regression bug. It needs to be investigated later. 15 16 type systemdNotifyMsg string 17 18 const ( 19 readyMsg systemdNotifyMsg = "READY=1" 20 stoppingMsg systemdNotifyMsg = "STOPPING=1" 21 reloadingMsg systemdNotifyMsg = "RELOADING=1" 22 watchdogMsg systemdNotifyMsg = "WATCHDOG=1" 23 ) 24 25 func statusMsg(msg string) systemdNotifyMsg { 26 return systemdNotifyMsg("STATUS=" + msg) 27 } 28 29 // Manager manages the graceful shutdown process 30 type Manager struct { 31 ctx context.Context 32 isChild bool 33 forked bool 34 lock sync.RWMutex 35 state state 36 shutdownCtx context.Context 37 hammerCtx context.Context 38 terminateCtx context.Context 39 managerCtx context.Context 40 shutdownCtxCancel context.CancelFunc 41 hammerCtxCancel context.CancelFunc 42 terminateCtxCancel context.CancelFunc 43 managerCtxCancel context.CancelFunc 44 runningServerWaitGroup sync.WaitGroup 45 terminateWaitGroup sync.WaitGroup 46 createServerCond sync.Cond 47 createdServer int 48 shutdownRequested chan struct{} 49 50 toRunAtShutdown []func() 51 toRunAtTerminate []func() 52 } 53 54 func newGracefulManager(ctx context.Context) *Manager { 55 manager := &Manager{ctx: ctx, shutdownRequested: make(chan struct{})} 56 manager.createServerCond.L = &sync.Mutex{} 57 manager.prepare(ctx) 58 manager.start() 59 return manager 60 } 61 62 func (g *Manager) prepare(ctx context.Context) { 63 g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx) 64 g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx) 65 g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx) 66 g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx) 67 68 g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate")) 69 g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown")) 70 g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer")) 71 g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager")) 72 73 if !g.setStateTransition(stateInit, stateRunning) { 74 panic("invalid graceful manager state: transition from init to running failed") 75 } 76 } 77 78 // DoImmediateHammer causes an immediate hammer 79 func (g *Manager) DoImmediateHammer() { 80 g.notify(statusMsg("Sending immediate hammer")) 81 g.doHammerTime(0 * time.Second) 82 } 83 84 // DoGracefulShutdown causes a graceful shutdown 85 func (g *Manager) DoGracefulShutdown() { 86 g.lock.Lock() 87 select { 88 case <-g.shutdownRequested: 89 default: 90 close(g.shutdownRequested) 91 } 92 forked := g.forked 93 g.lock.Unlock() 94 95 if !forked { 96 g.notify(stoppingMsg) 97 } else { 98 g.notify(statusMsg("Shutting down after fork")) 99 } 100 g.doShutdown() 101 } 102 103 // RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die. 104 // Any call to RegisterServer must be matched by a call to ServerDone 105 func (g *Manager) RegisterServer() { 106 KillParent() 107 g.runningServerWaitGroup.Add(1) 108 }