summaryrefslogtreecommitdiffstats
path: root/modules/graceful/manager_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/graceful/manager_windows.go')
-rw-r--r--modules/graceful/manager_windows.go190
1 files changed, 190 insertions, 0 deletions
diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go
new file mode 100644
index 0000000..bee4438
--- /dev/null
+++ b/modules/graceful/manager_windows.go
@@ -0,0 +1,190 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+
+//go:build windows
+
+package graceful
+
+import (
+ "os"
+ "runtime/pprof"
+ "strconv"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+
+ "golang.org/x/sys/windows/svc"
+ "golang.org/x/sys/windows/svc/debug"
+)
+
+// WindowsServiceName is the name of the Windows service
+var WindowsServiceName = "gitea"
+
+const (
+ hammerCode = 128
+ hammerCmd = svc.Cmd(hammerCode)
+ acceptHammerCode = svc.Accepted(hammerCode)
+)
+
+func (g *Manager) start() {
+ // Now label this and all goroutines created by this goroutine with the gracefulLifecycle manager
+ pprof.SetGoroutineLabels(g.managerCtx)
+ defer pprof.SetGoroutineLabels(g.ctx)
+
+ if skip, _ := strconv.ParseBool(os.Getenv("SKIP_MINWINSVC")); skip {
+ log.Trace("Skipping SVC check as SKIP_MINWINSVC is set")
+ return
+ }
+
+ // Make SVC process
+ run := svc.Run
+
+ //lint:ignore SA1019 We use IsAnInteractiveSession because IsWindowsService has a different permissions profile
+ isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck
+ if err != nil {
+ log.Error("Unable to ascertain if running as an Windows Service: %v", err)
+ return
+ }
+ if isAnInteractiveSession {
+ log.Trace("Not running a service ... using the debug SVC manager")
+ run = debug.Run
+ }
+ go func() {
+ _ = run(WindowsServiceName, g)
+ }()
+}
+
+// Execute makes Manager implement svc.Handler
+func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
+ if setting.StartupTimeout > 0 {
+ status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
+ } else {
+ status <- svc.Status{State: svc.StartPending}
+ }
+
+ log.Trace("Awaiting server start-up")
+ // Now need to wait for everything to start...
+ if !g.awaitServer(setting.StartupTimeout) {
+ log.Trace("... start-up failed ... Stopped")
+ return false, 1
+ }
+
+ log.Trace("Sending Running state to SVC")
+
+ // We need to implement some way of svc.AcceptParamChange/svc.ParamChange
+ status <- svc.Status{
+ State: svc.Running,
+ Accepts: svc.AcceptStop | svc.AcceptShutdown | acceptHammerCode,
+ }
+
+ log.Trace("Started")
+
+ waitTime := 30 * time.Second
+
+loop:
+ for {
+ select {
+ case <-g.ctx.Done():
+ log.Trace("Shutting down")
+ g.DoGracefulShutdown()
+ waitTime += setting.GracefulHammerTime
+ break loop
+ case <-g.shutdownRequested:
+ log.Trace("Shutting down")
+ waitTime += setting.GracefulHammerTime
+ break loop
+ case change := <-changes:
+ switch change.Cmd {
+ case svc.Interrogate:
+ log.Trace("SVC sent interrogate")
+ status <- change.CurrentStatus
+ case svc.Stop, svc.Shutdown:
+ log.Trace("SVC requested shutdown - shutting down")
+ g.DoGracefulShutdown()
+ waitTime += setting.GracefulHammerTime
+ break loop
+ case hammerCode:
+ log.Trace("SVC requested hammer - shutting down and hammering immediately")
+ g.DoGracefulShutdown()
+ g.DoImmediateHammer()
+ break loop
+ default:
+ log.Debug("Unexpected control request: %v", change.Cmd)
+ }
+ }
+ }
+
+ log.Trace("Sending StopPending state to SVC")
+ status <- svc.Status{
+ State: svc.StopPending,
+ WaitHint: uint32(waitTime / time.Millisecond),
+ }
+
+hammerLoop:
+ for {
+ select {
+ case change := <-changes:
+ switch change.Cmd {
+ case svc.Interrogate:
+ log.Trace("SVC sent interrogate")
+ status <- change.CurrentStatus
+ case svc.Stop, svc.Shutdown, hammerCmd:
+ log.Trace("SVC requested hammer - hammering immediately")
+ g.DoImmediateHammer()
+ break hammerLoop
+ default:
+ log.Debug("Unexpected control request: %v", change.Cmd)
+ }
+ case <-g.hammerCtx.Done():
+ break hammerLoop
+ }
+ }
+
+ log.Trace("Stopped")
+ return false, 0
+}
+
+func (g *Manager) awaitServer(limit time.Duration) bool {
+ c := make(chan struct{})
+ go func() {
+ g.createServerCond.L.Lock()
+ for {
+ if g.createdServer >= numberOfServersToCreate {
+ g.createServerCond.L.Unlock()
+ close(c)
+ return
+ }
+ select {
+ case <-g.IsShutdown():
+ g.createServerCond.L.Unlock()
+ return
+ default:
+ }
+ g.createServerCond.Wait()
+ }
+ }()
+
+ var tc <-chan time.Time
+ if limit > 0 {
+ tc = time.After(limit)
+ }
+ select {
+ case <-c:
+ return true // completed normally
+ case <-tc:
+ return false // timed out
+ case <-g.IsShutdown():
+ g.createServerCond.Signal()
+ return false
+ }
+}
+
+func (g *Manager) notify(msg systemdNotifyMsg) {
+ // Windows doesn't use systemd to notify
+}
+
+func KillParent() {
+ // Windows doesn't need to "kill parent" because there is no graceful restart
+}