mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-05-14 05:52:43 +00:00
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
This commit is contained in:
parent
d7ac9727bb
commit
cbaa1de9ec
30 changed files with 666 additions and 497 deletions
|
@ -1,5 +1,3 @@
|
|||
// +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.
|
||||
|
@ -19,37 +17,16 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
type state uint8
|
||||
|
||||
const (
|
||||
stateInit state = iota
|
||||
stateRunning
|
||||
stateShuttingDown
|
||||
stateTerminate
|
||||
)
|
||||
|
||||
var (
|
||||
// RWMutex for when adding servers or shutting down
|
||||
runningServerReg sync.RWMutex
|
||||
runningServerWG sync.WaitGroup
|
||||
// ensure we only fork once
|
||||
runningServersForked bool
|
||||
|
||||
// DefaultReadTimeOut default read timeout
|
||||
DefaultReadTimeOut time.Duration
|
||||
// DefaultWriteTimeOut default write timeout
|
||||
DefaultWriteTimeOut time.Duration
|
||||
// DefaultMaxHeaderBytes default max header bytes
|
||||
DefaultMaxHeaderBytes int
|
||||
|
||||
// IsChild reports if we are a fork iff LISTEN_FDS is set and our parent PID is not 1
|
||||
IsChild = len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1
|
||||
)
|
||||
|
||||
func init() {
|
||||
runningServerReg = sync.RWMutex{}
|
||||
runningServerWG = sync.WaitGroup{}
|
||||
|
||||
DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
|
||||
}
|
||||
|
||||
|
@ -58,43 +35,29 @@ type ServeFunction = func(net.Listener) error
|
|||
|
||||
// Server represents our graceful server
|
||||
type Server struct {
|
||||
network string
|
||||
address string
|
||||
listener net.Listener
|
||||
PreSignalHooks map[os.Signal][]func()
|
||||
PostSignalHooks map[os.Signal][]func()
|
||||
wg sync.WaitGroup
|
||||
sigChan chan os.Signal
|
||||
state state
|
||||
lock *sync.RWMutex
|
||||
BeforeBegin func(network, address string)
|
||||
OnShutdown func()
|
||||
}
|
||||
|
||||
// WaitForServers waits for all running servers to finish
|
||||
func WaitForServers() {
|
||||
runningServerWG.Wait()
|
||||
network string
|
||||
address string
|
||||
listener net.Listener
|
||||
wg sync.WaitGroup
|
||||
state state
|
||||
lock *sync.RWMutex
|
||||
BeforeBegin func(network, address string)
|
||||
OnShutdown func()
|
||||
}
|
||||
|
||||
// NewServer creates a server on network at provided address
|
||||
func NewServer(network, address string) *Server {
|
||||
runningServerReg.Lock()
|
||||
defer runningServerReg.Unlock()
|
||||
|
||||
if IsChild {
|
||||
if Manager.IsChild() {
|
||||
log.Info("Restarting new server: %s:%s on PID: %d", network, address, os.Getpid())
|
||||
} else {
|
||||
log.Info("Starting new server: %s:%s on PID: %d", network, address, os.Getpid())
|
||||
}
|
||||
srv := &Server{
|
||||
wg: sync.WaitGroup{},
|
||||
sigChan: make(chan os.Signal),
|
||||
PreSignalHooks: map[os.Signal][]func(){},
|
||||
PostSignalHooks: map[os.Signal][]func(){},
|
||||
state: stateInit,
|
||||
lock: &sync.RWMutex{},
|
||||
network: network,
|
||||
address: address,
|
||||
wg: sync.WaitGroup{},
|
||||
state: stateInit,
|
||||
lock: &sync.RWMutex{},
|
||||
network: network,
|
||||
address: address,
|
||||
}
|
||||
|
||||
srv.BeforeBegin = func(network, addr string) {
|
||||
|
@ -107,7 +70,7 @@ func NewServer(network, address string) *Server {
|
|||
// ListenAndServe listens on the provided network address and then calls Serve
|
||||
// to handle requests on incoming connections.
|
||||
func (srv *Server) ListenAndServe(serve ServeFunction) error {
|
||||
go srv.handleSignals()
|
||||
go srv.awaitShutdown()
|
||||
|
||||
l, err := GetListener(srv.network, srv.address)
|
||||
if err != nil {
|
||||
|
@ -117,8 +80,6 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {
|
|||
|
||||
srv.listener = newWrappedListener(l, srv)
|
||||
|
||||
KillParent()
|
||||
|
||||
srv.BeforeBegin(srv.network, srv.address)
|
||||
|
||||
return srv.Serve(serve)
|
||||
|
@ -150,7 +111,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFuncti
|
|||
// ListenAndServeTLSConfig listens on the provided network address and then calls
|
||||
// Serve to handle requests on incoming TLS connections.
|
||||
func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
|
||||
go srv.handleSignals()
|
||||
go srv.awaitShutdown()
|
||||
|
||||
l, err := GetListener(srv.network, srv.address)
|
||||
if err != nil {
|
||||
|
@ -161,7 +122,6 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
|
|||
wl := newWrappedListener(l, srv)
|
||||
srv.listener = tls.NewListener(wl, tlsConfig)
|
||||
|
||||
KillParent()
|
||||
srv.BeforeBegin(srv.network, srv.address)
|
||||
|
||||
return srv.Serve(serve)
|
||||
|
@ -178,12 +138,12 @@ func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFun
|
|||
func (srv *Server) Serve(serve ServeFunction) error {
|
||||
defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid())
|
||||
srv.setState(stateRunning)
|
||||
runningServerWG.Add(1)
|
||||
Manager.RegisterServer()
|
||||
err := serve(srv.listener)
|
||||
log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid())
|
||||
srv.wg.Wait()
|
||||
srv.setState(stateTerminate)
|
||||
runningServerWG.Done()
|
||||
Manager.ServerDone()
|
||||
// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
|
||||
if err != nil && strings.Contains(err.Error(), "use of closed") {
|
||||
return nil
|
||||
|
@ -205,6 +165,10 @@ func (srv *Server) setState(st state) {
|
|||
srv.state = st
|
||||
}
|
||||
|
||||
type filer interface {
|
||||
File() (*os.File, error)
|
||||
}
|
||||
|
||||
type wrappedListener struct {
|
||||
net.Listener
|
||||
stopped bool
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue