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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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.Warn("PID %d. Received SIGUSR1. Releasing and reopening logs", pid)
  105. if err := log.ReleaseReopen(); err != nil {
  106. log.Error("Error whilst releasing and reopening logs: %v", err)
  107. }
  108. case syscall.SIGUSR2:
  109. log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
  110. g.DoImmediateHammer()
  111. case syscall.SIGINT:
  112. log.Warn("PID %d. Received SIGINT. Shutting down...", pid)
  113. g.DoGracefulShutdown()
  114. case syscall.SIGTERM:
  115. log.Warn("PID %d. Received SIGTERM. Shutting down...", pid)
  116. g.DoGracefulShutdown()
  117. case syscall.SIGTSTP:
  118. log.Info("PID %d. Received SIGTSTP.", pid)
  119. default:
  120. log.Info("PID %d. Received %v.", pid, sig)
  121. }
  122. case <-ctx.Done():
  123. log.Warn("PID: %d. Background context for manager closed - %v - Shutting down...", pid, ctx.Err())
  124. g.DoGracefulShutdown()
  125. }
  126. }
  127. }
  128. func (g *Manager) doFork() error {
  129. g.lock.Lock()
  130. if g.forked {
  131. g.lock.Unlock()
  132. return errors.New("another process already forked. Ignoring this one")
  133. }
  134. g.forked = true
  135. g.lock.Unlock()
  136. // We need to move the file logs to append pids
  137. setting.RestartLogsWithPIDSuffix()
  138. _, err := RestartProcess()
  139. return err
  140. }
  141. // DoGracefulRestart causes a graceful restart
  142. func (g *Manager) DoGracefulRestart() {
  143. if setting.GracefulRestartable {
  144. log.Info("PID: %d. Forking...", os.Getpid())
  145. err := g.doFork()
  146. if err != nil && err.Error() != "another process already forked. Ignoring this one" {
  147. log.Error("Error whilst forking from PID: %d : %v", os.Getpid(), err)
  148. }
  149. } else {
  150. log.Info("PID: %d. Not set restartable. Shutting down...", os.Getpid())
  151. g.doShutdown()
  152. }
  153. }
  154. // DoImmediateHammer causes an immediate hammer
  155. func (g *Manager) DoImmediateHammer() {
  156. g.doHammerTime(0 * time.Second)
  157. }
  158. // DoGracefulShutdown causes a graceful shutdown
  159. func (g *Manager) DoGracefulShutdown() {
  160. g.doShutdown()
  161. }
  162. // RegisterServer registers the running of a listening server, in the case of unix this means that the parent process can now die.
  163. // Any call to RegisterServer must be matched by a call to ServerDone
  164. func (g *Manager) RegisterServer() {
  165. KillParent()
  166. g.runningServerWaitGroup.Add(1)
  167. }