diff options
author | zeripath <art27@cantab.net> | 2019-11-21 18:32:02 +0000 |
---|---|---|
committer | techknowlogick <techknowlogick@gitea.io> | 2019-11-21 13:32:02 -0500 |
commit | cbaa1de9ec8ab1baa49357b660fab16a68097c84 (patch) | |
tree | 5f481d73c95ae24b91e7bb03abaa5cf921f806b5 /modules/graceful/manager_unix.go | |
parent | d7ac9727bb5046118915cbb26b2dac1b7b27c9d4 (diff) | |
download | gitea-cbaa1de9ec8ab1baa49357b660fab16a68097c84.tar.gz gitea-cbaa1de9ec8ab1baa49357b660fab16a68097c84.zip |
Add Graceful shutdown for Windows and hooks for shutdown of goroutines (#8964)
* Graceful Shutdown for windows and others
Restructures modules/graceful, adding shutdown for windows, removing and
replacing the old minwinsvc code.
Creates a new waitGroup - terminate which allows for goroutines to
finish up after the shutdown of the servers.
Shutdown and terminate hooks are added for goroutines.
* Remove unused functions - these can be added in a different PR
* Add startup timeout functionality
* Document STARTUP_TIMEOUT
Diffstat (limited to 'modules/graceful/manager_unix.go')
-rw-r--r-- | modules/graceful/manager_unix.go | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go new file mode 100644 index 0000000000..15b0ff4448 --- /dev/null +++ b/modules/graceful/manager_unix.go @@ -0,0 +1,141 @@ +// +build !windows + +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package graceful + +import ( + "errors" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" +) + +type gracefulManager struct { + isChild bool + forked bool + lock *sync.RWMutex + state state + shutdown chan struct{} + hammer chan struct{} + terminate chan struct{} + runningServerWaitGroup sync.WaitGroup + createServerWaitGroup sync.WaitGroup + terminateWaitGroup sync.WaitGroup +} + +func newGracefulManager() *gracefulManager { + manager := &gracefulManager{ + isChild: len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1, + lock: &sync.RWMutex{}, + } + manager.createServerWaitGroup.Add(numberOfServersToCreate) + manager.Run() + return manager +} + +func (g *gracefulManager) Run() { + g.setState(stateRunning) + go g.handleSignals() + c := make(chan struct{}) + go func() { + defer close(c) + // Wait till we're done getting all of the listeners and then close + // the unused ones + g.createServerWaitGroup.Wait() + // Ignore the error here there's not much we can do with it + // They're logged in the CloseProvidedListeners function + _ = CloseProvidedListeners() + }() + if setting.StartupTimeout > 0 { + go func() { + select { + case <-c: + return + case <-g.IsShutdown(): + return + case <-time.After(setting.StartupTimeout): + log.Error("Startup took too long! Shutting down") + g.doShutdown() + } + }() + } +} + +func (g *gracefulManager) handleSignals() { + var sig os.Signal + + signalChannel := make(chan os.Signal, 1) + + signal.Notify( + signalChannel, + syscall.SIGHUP, + syscall.SIGUSR1, + syscall.SIGUSR2, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGTSTP, + ) + + pid := syscall.Getpid() + for { + sig = <-signalChannel + switch sig { + case syscall.SIGHUP: + if setting.GracefulRestartable { + log.Info("PID: %d. Received SIGHUP. Forking...", pid) + err := g.doFork() + if err != nil && err.Error() != "another process already forked. Ignoring this one" { + log.Error("Error whilst forking from PID: %d : %v", pid, err) + } + } else { + log.Info("PID: %d. Received SIGHUP. Not set restartable. Shutting down...", pid) + + g.doShutdown() + } + case syscall.SIGUSR1: + log.Info("PID %d. Received SIGUSR1.", pid) + case syscall.SIGUSR2: + log.Warn("PID %d. Received SIGUSR2. Hammering...", pid) + g.doHammerTime(0 * time.Second) + case syscall.SIGINT: + log.Warn("PID %d. Received SIGINT. Shutting down...", pid) + g.doShutdown() + case syscall.SIGTERM: + log.Warn("PID %d. Received SIGTERM. Shutting down...", pid) + g.doShutdown() + case syscall.SIGTSTP: + log.Info("PID %d. Received SIGTSTP.", pid) + default: + log.Info("PID %d. Received %v.", pid, sig) + } + } +} + +func (g *gracefulManager) doFork() error { + g.lock.Lock() + if g.forked { + g.lock.Unlock() + return errors.New("another process already forked. Ignoring this one") + } + g.forked = true + g.lock.Unlock() + // We need to move the file logs to append pids + setting.RestartLogsWithPIDSuffix() + + _, err := RestartProcess() + + return err +} + +func (g *gracefulManager) RegisterServer() { + KillParent() + g.runningServerWaitGroup.Add(1) +} |