You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager_unix.go 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. //go:build !windows
  5. package graceful
  6. import (
  7. "context"
  8. "errors"
  9. "os"
  10. "os/signal"
  11. "runtime/pprof"
  12. "sync"
  13. "syscall"
  14. "time"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/process"
  17. "code.gitea.io/gitea/modules/setting"
  18. )
  19. // Manager manages the graceful shutdown process
  20. type Manager struct {
  21. isChild bool
  22. forked bool
  23. lock *sync.RWMutex
  24. state state
  25. shutdownCtx context.Context
  26. hammerCtx context.Context
  27. terminateCtx context.Context
  28. managerCtx context.Context
  29. shutdownCtxCancel context.CancelFunc
  30. hammerCtxCancel context.CancelFunc
  31. terminateCtxCancel context.CancelFunc
  32. managerCtxCancel context.CancelFunc
  33. runningServerWaitGroup sync.WaitGroup
  34. createServerWaitGroup sync.WaitGroup
  35. terminateWaitGroup sync.WaitGroup
  36. toRunAtShutdown []func()
  37. toRunAtHammer []func()
  38. toRunAtTerminate []func()
  39. }
  40. func newGracefulManager(ctx context.Context) *Manager {
  41. manager := &Manager{
  42. isChild: len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1,
  43. lock: &sync.RWMutex{},
  44. }
  45. manager.createServerWaitGroup.Add(numberOfServersToCreate)
  46. manager.start(ctx)
  47. return manager
  48. }
  49. func (g *Manager) start(ctx context.Context) {
  50. // Make contexts
  51. g.terminateCtx, g.terminateCtxCancel = context.WithCancel(ctx)
  52. g.shutdownCtx, g.shutdownCtxCancel = context.WithCancel(ctx)
  53. g.hammerCtx, g.hammerCtxCancel = context.WithCancel(ctx)
  54. g.managerCtx, g.managerCtxCancel = context.WithCancel(ctx)
  55. // Next add pprof labels to these contexts
  56. g.terminateCtx = pprof.WithLabels(g.terminateCtx, pprof.Labels("graceful-lifecycle", "with-terminate"))
  57. g.shutdownCtx = pprof.WithLabels(g.shutdownCtx, pprof.Labels("graceful-lifecycle", "with-shutdown"))
  58. g.hammerCtx = pprof.WithLabels(g.hammerCtx, pprof.Labels("graceful-lifecycle", "with-hammer"))
  59. g.managerCtx = pprof.WithLabels(g.managerCtx, pprof.Labels("graceful-lifecycle", "with-manager"))
  60. // Now label this and all goroutines created by this goroutine with the graceful-lifecycle manager
  61. pprof.SetGoroutineLabels(g.managerCtx)
  62. defer pprof.SetGoroutineLabels(ctx)
  63. // Set the running state & handle signals
  64. g.setState(stateRunning)
  65. go g.handleSignals(g.managerCtx)
  66. // Handle clean up of unused provided listeners and delayed start-up
  67. startupDone := make(chan struct{})
  68. go func() {
  69. defer close(startupDone)
  70. // Wait till we're done getting all of the listeners and then close
  71. // the unused ones
  72. g.createServerWaitGroup.Wait()
  73. // Ignore the error here there's not much we can do with it
  74. // They're logged in the CloseProvidedListeners function
  75. _ = CloseProvidedListeners()
  76. }()
  77. if setting.StartupTimeout > 0 {
  78. go func() {
  79. select {
  80. case <-startupDone:
  81. return
  82. case <-g.IsShutdown():
  83. func() {
  84. // When waitgroup counter goes negative it will panic - we don't care about this so we can just ignore it.
  85. defer func() {
  86. _ = recover()
  87. }()
  88. // Ensure that the createServerWaitGroup stops waiting
  89. for {
  90. g.createServerWaitGroup.Done()
  91. }
  92. }()
  93. return
  94. case <-time.After(setting.StartupTimeout):
  95. log.Error("Startup took too long! Shutting down")
  96. g.doShutdown()
  97. }
  98. }()
  99. }
  100. }
  101. func (g *Manager) handleSignals(ctx context.Context) {
  102. ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Graceful: HandleSignals", process.SystemProcessType, true)
  103. defer finished()
  104. signalChannel := make(chan os.Signal, 1)
  105. signal.Notify(
  106. signalChannel,
  107. syscall.SIGHUP,
  108. syscall.SIGUSR1,
  109. syscall.SIGUSR2,
  110. syscall.SIGINT,
  111. syscall.SIGTERM,
  112. syscall.SIGTSTP,
  113. )
  114. pid := syscall.Getpid()
  115. for {
  116. select {
  117. case sig := <-signalChannel:
  118. switch sig {
  119. case syscall.SIGHUP:
  120. log.Info("PID: %d. Received SIGHUP. Attempting GracefulRestart...", pid)
  121. g.DoGracefulRestart()
  122. case syscall.SIGUSR1:
  123. log.Warn("PID %d. Received SIGUSR1. Releasing and reopening logs", pid)
  124. if err := log.ReleaseReopen(); err != nil {
  125. log.Error("Error whilst releasing and reopening logs: %v", err)
  126. }
  127. case syscall.SIGUSR2:
  128. log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
  129. g.DoImmediateHammer()
  130. case syscall.SIGINT:
  131. log.Warn("PID %d. Received SIGINT. Shutting down...", pid)
  132. g.DoGracefulShutdown()
  133. case syscall.SIGTERM:
  134. log.Warn("PID %d. Received SIGTERM. Shutting down...", pid)
  135. g.DoGracefulShutdown()
  136. case syscall.SIGTSTP:
  137. log.Info("PID %d. Received SIGTSTP.", pid)
  138. default:
  139. log.Info("PID %d. Received %v.", pid, sig)
  140. }
  141. case <-ctx.Done():
  142. log.Warn("PID: %d. Background context for manager closed - %v - Shutting down...", pid, ctx.Err())
  143. g.DoGracefulShutdown()
  144. }
  145. }
  146. }
  147. func (g *Manager) doFork() error {
  148. g.lock.Lock()
  149. if g.forked {
  150. g.lock.Unlock()
  151. return errors.New("another process already forked. Ignoring this one")
  152. }
  153. g.forked = true
  154. g.lock.Unlock()
  155. // We need to move the file logs to append pids
  156. setting.RestartLogsWithPIDSuffix()
  157. _, err := RestartProcess()
  158. return err
  159. }
  160. // DoGracefulRestart causes a graceful restart
  161. func (g *Manager) DoGracefulRestart() {
  162. if setting.GracefulRestartable {
  163. log.Info("PID: %d. Forking...", os.Getpid())
  164. err := g.doFork()
  165. if err != nil {
  166. if err.Error() == "another process already forked. Ignoring this one" {
  167. g.DoImmediateHammer()
  168. } else {
  169. log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err)
  170. }
  171. }
  172. } else {
  173. log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid())
  174. g.doShutdown()
  175. }
  176. }
  177. // DoImmediateHammer causes an immediate hammer
  178. func (g *Manager) DoImmediateHammer() {
  179. g.doHammerTime(0 * time.Second)
  180. }
  181. // DoGracefulShutdown causes a graceful shutdown
  182. func (g *Manager) DoGracefulShutdown() {
  183. g.doShutdown()
  184. }
  185. // RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die.
  186. // Any call to RegisterServer must be matched by a call to ServerDone
  187. func (g *Manager) RegisterServer() {
  188. KillParent()
  189. g.runningServerWaitGroup.Add(1)
  190. }