mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	* 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
		
			
				
	
	
		
			188 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 (
 | 
						|
	"time"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
	"code.gitea.io/gitea/modules/setting"
 | 
						|
)
 | 
						|
 | 
						|
type state uint8
 | 
						|
 | 
						|
const (
 | 
						|
	stateInit state = iota
 | 
						|
	stateRunning
 | 
						|
	stateShuttingDown
 | 
						|
	stateTerminate
 | 
						|
)
 | 
						|
 | 
						|
// There are three places that could inherit sockets:
 | 
						|
//
 | 
						|
// * HTTP or HTTPS main listener
 | 
						|
// * HTTP redirection fallback
 | 
						|
// * SSH
 | 
						|
//
 | 
						|
// If you add an additional place you must increment this number
 | 
						|
// and add a function to call manager.InformCleanup if it's not going to be used
 | 
						|
const numberOfServersToCreate = 3
 | 
						|
 | 
						|
// Manager represents the graceful server manager interface
 | 
						|
var Manager *gracefulManager
 | 
						|
 | 
						|
func init() {
 | 
						|
	Manager = newGracefulManager()
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) doShutdown() {
 | 
						|
	if !g.setStateTransition(stateRunning, stateShuttingDown) {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	g.lock.Lock()
 | 
						|
	close(g.shutdown)
 | 
						|
	g.lock.Unlock()
 | 
						|
 | 
						|
	if setting.GracefulHammerTime >= 0 {
 | 
						|
		go g.doHammerTime(setting.GracefulHammerTime)
 | 
						|
	}
 | 
						|
	go func() {
 | 
						|
		g.WaitForServers()
 | 
						|
		<-time.After(1 * time.Second)
 | 
						|
		g.doTerminate()
 | 
						|
	}()
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) doHammerTime(d time.Duration) {
 | 
						|
	time.Sleep(d)
 | 
						|
	select {
 | 
						|
	case <-g.hammer:
 | 
						|
	default:
 | 
						|
		log.Warn("Setting Hammer condition")
 | 
						|
		close(g.hammer)
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) doTerminate() {
 | 
						|
	if !g.setStateTransition(stateShuttingDown, stateTerminate) {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	g.lock.Lock()
 | 
						|
	close(g.terminate)
 | 
						|
	g.lock.Unlock()
 | 
						|
}
 | 
						|
 | 
						|
// IsChild returns if the current process is a child of previous Gitea process
 | 
						|
func (g *gracefulManager) IsChild() bool {
 | 
						|
	return g.isChild
 | 
						|
}
 | 
						|
 | 
						|
// IsShutdown returns a channel which will be closed at shutdown.
 | 
						|
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
 | 
						|
func (g *gracefulManager) IsShutdown() <-chan struct{} {
 | 
						|
	g.lock.RLock()
 | 
						|
	if g.shutdown == nil {
 | 
						|
		g.lock.RUnlock()
 | 
						|
		g.lock.Lock()
 | 
						|
		if g.shutdown == nil {
 | 
						|
			g.shutdown = make(chan struct{})
 | 
						|
		}
 | 
						|
		defer g.lock.Unlock()
 | 
						|
		return g.shutdown
 | 
						|
	}
 | 
						|
	defer g.lock.RUnlock()
 | 
						|
	return g.shutdown
 | 
						|
}
 | 
						|
 | 
						|
// IsHammer returns a channel which will be closed at hammer
 | 
						|
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
 | 
						|
// Servers running within the running server wait group should respond to IsHammer
 | 
						|
// if not shutdown already
 | 
						|
func (g *gracefulManager) IsHammer() <-chan struct{} {
 | 
						|
	g.lock.RLock()
 | 
						|
	if g.hammer == nil {
 | 
						|
		g.lock.RUnlock()
 | 
						|
		g.lock.Lock()
 | 
						|
		if g.hammer == nil {
 | 
						|
			g.hammer = make(chan struct{})
 | 
						|
		}
 | 
						|
		defer g.lock.Unlock()
 | 
						|
		return g.hammer
 | 
						|
	}
 | 
						|
	defer g.lock.RUnlock()
 | 
						|
	return g.hammer
 | 
						|
}
 | 
						|
 | 
						|
// IsTerminate returns a channel which will be closed at terminate
 | 
						|
// The order of closure is IsShutdown, IsHammer (potentially), IsTerminate
 | 
						|
// IsTerminate will only close once all running servers have stopped
 | 
						|
func (g *gracefulManager) IsTerminate() <-chan struct{} {
 | 
						|
	g.lock.RLock()
 | 
						|
	if g.terminate == nil {
 | 
						|
		g.lock.RUnlock()
 | 
						|
		g.lock.Lock()
 | 
						|
		if g.terminate == nil {
 | 
						|
			g.terminate = make(chan struct{})
 | 
						|
		}
 | 
						|
		defer g.lock.Unlock()
 | 
						|
		return g.terminate
 | 
						|
	}
 | 
						|
	defer g.lock.RUnlock()
 | 
						|
	return g.terminate
 | 
						|
}
 | 
						|
 | 
						|
// ServerDone declares a running server done and subtracts one from the
 | 
						|
// running server wait group. Users probably do not want to call this
 | 
						|
// and should use one of the RunWithShutdown* functions
 | 
						|
func (g *gracefulManager) ServerDone() {
 | 
						|
	g.runningServerWaitGroup.Done()
 | 
						|
}
 | 
						|
 | 
						|
// WaitForServers waits for all running servers to finish. Users should probably
 | 
						|
// instead use AtTerminate or IsTerminate
 | 
						|
func (g *gracefulManager) WaitForServers() {
 | 
						|
	g.runningServerWaitGroup.Wait()
 | 
						|
}
 | 
						|
 | 
						|
// WaitForTerminate waits for all terminating actions to finish.
 | 
						|
// Only the main go-routine should use this
 | 
						|
func (g *gracefulManager) WaitForTerminate() {
 | 
						|
	g.terminateWaitGroup.Wait()
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) getState() state {
 | 
						|
	g.lock.RLock()
 | 
						|
	defer g.lock.RUnlock()
 | 
						|
	return g.state
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) setStateTransition(old, new state) bool {
 | 
						|
	if old != g.getState() {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	g.lock.Lock()
 | 
						|
	if g.state != old {
 | 
						|
		g.lock.Unlock()
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	g.state = new
 | 
						|
	g.lock.Unlock()
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
func (g *gracefulManager) setState(st state) {
 | 
						|
	g.lock.Lock()
 | 
						|
	defer g.lock.Unlock()
 | 
						|
 | 
						|
	g.state = st
 | 
						|
}
 | 
						|
 | 
						|
// InformCleanup tells the cleanup wait group that we have either taken a listener
 | 
						|
// or will not be taking a listener
 | 
						|
func (g *gracefulManager) InformCleanup() {
 | 
						|
	g.createServerWaitGroup.Done()
 | 
						|
}
 |