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.

184 lines
4.7 KiB

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