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

manager_windows.go 3.8 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
  6. package graceful
  7. import (
  8. "os"
  9. "strconv"
  10. "sync"
  11. "time"
  12. "code.gitea.io/gitea/modules/log"
  13. "code.gitea.io/gitea/modules/setting"
  14. "golang.org/x/sys/windows/svc"
  15. "golang.org/x/sys/windows/svc/debug"
  16. )
  17. var WindowsServiceName = "gitea"
  18. const (
  19. hammerCode = 128
  20. hammerCmd = svc.Cmd(hammerCode)
  21. acceptHammerCode = svc.Accepted(hammerCode)
  22. )
  23. type gracefulManager struct {
  24. isChild bool
  25. lock *sync.RWMutex
  26. state state
  27. shutdown chan struct{}
  28. hammer chan struct{}
  29. terminate chan struct{}
  30. runningServerWaitGroup sync.WaitGroup
  31. createServerWaitGroup sync.WaitGroup
  32. terminateWaitGroup sync.WaitGroup
  33. }
  34. func newGracefulManager() *gracefulManager {
  35. manager := &gracefulManager{
  36. isChild: false,
  37. lock: &sync.RWMutex{},
  38. }
  39. manager.createServerWaitGroup.Add(numberOfServersToCreate)
  40. manager.Run()
  41. return manager
  42. }
  43. func (g *gracefulManager) Run() {
  44. g.setState(stateRunning)
  45. if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
  46. return
  47. }
  48. run := svc.Run
  49. isInteractive, err := svc.IsAnInteractiveSession()
  50. if err != nil {
  51. log.Error("Unable to ascertain if running as an Interactive Session: %v", err)
  52. return
  53. }
  54. if isInteractive {
  55. run = debug.Run
  56. }
  57. go run(WindowsServiceName, g)
  58. }
  59. // Execute makes gracefulManager implement svc.Handler
  60. func (g *gracefulManager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
  61. if setting.StartupTimeout > 0 {
  62. status <- svc.Status{State: svc.StartPending}
  63. } else {
  64. status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout/time.Millisecond)}
  65. }
  66. // Now need to wait for everything to start...
  67. if !g.awaitServer(setting.StartupTimeout) {
  68. return false, 1
  69. }
  70. // We need to implement some way of svc.AcceptParamChange/svc.ParamChange
  71. status <- svc.Status{
  72. State: svc.Running,
  73. Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
  74. }
  75. waitTime := 30 * time.Second
  76. loop:
  77. for change := range changes {
  78. switch change.Cmd {
  79. case svc.Interrogate:
  80. status <- change.CurrentStatus
  81. case svc.Stop, svc.Shutdown:
  82. g.doShutdown()
  83. waitTime += setting.GracefulHammerTime
  84. break loop
  85. case hammerCode:
  86. g.doShutdown()
  87. g.doHammerTime(0 *time.Second)
  88. break loop
  89. default:
  90. log.Debug("Unexpected control request: %v", change.Cmd)
  91. }
  92. }
  93. status <- svc.Status{
  94. State: svc.StopPending,
  95. WaitHint: uint32(waitTime/time.Millisecond),
  96. }
  97. hammerLoop:
  98. for {
  99. select {
  100. case change := <-changes:
  101. switch change.Cmd {
  102. case svc.Interrogate:
  103. status <- change.CurrentStatus
  104. case svc.Stop, svc.Shutdown, hammerCmd:
  105. g.doHammerTime(0 * time.Second)
  106. break hammerLoop
  107. default:
  108. log.Debug("Unexpected control request: %v", change.Cmd)
  109. }
  110. case <-g.hammer:
  111. break hammerLoop
  112. }
  113. }
  114. return false, 0
  115. }
  116. func (g *gracefulManager) RegisterServer() {
  117. g.runningServerWaitGroup.Add(1)
  118. }
  119. func (g *gracefulManager) awaitServer(limit time.Duration) bool {
  120. c := make(chan struct{})
  121. go func() {
  122. defer close(c)
  123. g.createServerWaitGroup.Wait()
  124. }()
  125. if limit > 0 {
  126. select {
  127. case <-c:
  128. return true // completed normally
  129. case <-time.After(limit):
  130. return false // timed out
  131. case <-g.IsShutdown():
  132. return false
  133. }
  134. } else {
  135. select {
  136. case <-c:
  137. return true // completed normally
  138. case <-g.IsShutdown():
  139. return false
  140. }
  141. }
  142. }