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 5.3KB

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