選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

manager_common.go 3.3KB

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