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 4.0 kB

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