mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-28 13:49:13 -05:00
60c5339042
* Graceful: Create callbacks to with contexts * Graceful: Say when Gitea is completely finished * Graceful: Git and Process within HammerTime Force all git commands to terminate at HammerTime Force all process commands to terminate at HammerTime Move almost all git processes to run as git Commands * Graceful: Always Hammer after Shutdown * ProcessManager: Add cancel functionality * Fix tests * Make sure that process.Manager.Kill() cancels * Make threadsafe access to Processes and remove own unused Kill * Remove cmd from the process manager as it is no longer used * the default context is the correct context * get rid of double till
145 lines
3.5 KiB
Go
145 lines
3.5 KiB
Go
// +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 (
|
|
"context"
|
|
"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(ctx context.Context) *gracefulManager {
|
|
manager := &gracefulManager{
|
|
isChild: len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1,
|
|
lock: &sync.RWMutex{},
|
|
}
|
|
manager.createServerWaitGroup.Add(numberOfServersToCreate)
|
|
manager.Run(ctx)
|
|
return manager
|
|
}
|
|
|
|
func (g *gracefulManager) Run(ctx context.Context) {
|
|
g.setState(stateRunning)
|
|
go g.handleSignals(ctx)
|
|
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(ctx context.Context) {
|
|
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 {
|
|
select {
|
|
case 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)
|
|
}
|
|
case <-ctx.Done():
|
|
log.Warn("PID: %d. Background context for manager closed - %v - Shutting down...", pid, ctx.Err())
|
|
g.doShutdown()
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|