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/server.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/server.go')
-rw-r--r-- | modules/graceful/server.go | 80 |
1 files changed, 22 insertions, 58 deletions
diff --git a/modules/graceful/server.go b/modules/graceful/server.go index 896d547b46..c6692cbb75 100644 --- a/modules/graceful/server.go +++ b/modules/graceful/server.go @@ -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 |