Browse Source

Expose graceful.ShutdownNow

This backends to the listener.DrainAll functionality I just added,
allowing impatient users to shut down their services immediately and
un-gracefully if they want.
Carl Jackson 11 years ago
parent
commit
624e61b879
1 changed files with 72 additions and 39 deletions
  1. +72
    -39
      graceful/signal.go

+ 72
- 39
graceful/signal.go View File

@ -19,10 +19,6 @@ var wait = make(chan struct{})
var stdSignals = []os.Signal{os.Interrupt} var stdSignals = []os.Signal{os.Interrupt}
var sigchan = make(chan os.Signal, 1) var sigchan = make(chan os.Signal, 1)
func init() {
go waitForSignal()
}
// HandleSignals installs signal handlers for a set of standard signals. By // HandleSignals installs signal handlers for a set of standard signals. By
// default, this set only includes keyboard interrupts, however when the package // default, this set only includes keyboard interrupts, however when the package
// detects that it is running under Einhorn, a SIGUSR2 handler is installed as // detects that it is running under Einhorn, a SIGUSR2 handler is installed as
@ -42,20 +38,6 @@ func ResetSignals() {
signal.Stop(sigchan) signal.Stop(sigchan)
} }
type userShutdown struct{}
func (u userShutdown) String() string {
return "application initiated shutdown"
}
func (u userShutdown) Signal() {}
// Shutdown manually triggers a shutdown from your application. Like Wait(),
// blocks until all connections have gracefully shut down.
func Shutdown() {
sigchan <- userShutdown{}
<-wait
}
// PreHook registers a function to be called before any of this package's normal // PreHook registers a function to be called before any of this package's normal
// shutdown actions. All listeners will be called in the order they were added, // shutdown actions. All listeners will be called in the order they were added,
// from a single goroutine. // from a single goroutine.
@ -81,38 +63,89 @@ func PostHook(f func()) {
posthooks = append(posthooks, f) posthooks = append(posthooks, f)
} }
func waitForSignal() {
<-sigchan
// Shutdown manually triggers a shutdown from your application. Like Wait,
// blocks until all connections have gracefully shut down.
func Shutdown() {
shutdown(false)
}
mu.Lock()
defer mu.Unlock()
// ShutdownNow triggers an immediate shutdown from your application. All
// connections (not just those that are idle) are immediately closed, even if
// they are in the middle of serving a request.
func ShutdownNow() {
shutdown(true)
}
// Wait for all connections to gracefully shut down. This is commonly called at
// the bottom of the main() function to prevent the program from exiting
// prematurely.
func Wait() {
<-wait
}
func init() {
go sigLoop()
}
func sigLoop() {
for {
<-sigchan
go shutdown(false)
}
}
for _, f := range prehooks {
f()
var preOnce, closeOnce, forceOnce, postOnce, notifyOnce sync.Once
func shutdown(force bool) {
preOnce.Do(func() {
mu.Lock()
defer mu.Unlock()
for _, f := range prehooks {
f()
}
})
if force {
forceOnce.Do(func() {
closeListeners(force)
})
} else {
closeOnce.Do(func() {
closeListeners(force)
})
} }
postOnce.Do(func() {
mu.Lock()
defer mu.Unlock()
for _, f := range posthooks {
f()
}
})
notifyOnce.Do(func() {
close(wait)
})
}
func closeListeners(force bool) {
atomic.StoreInt32(&closing, 1) atomic.StoreInt32(&closing, 1)
var wg sync.WaitGroup var wg sync.WaitGroup
defer wg.Wait()
mu.Lock()
defer mu.Unlock()
wg.Add(len(listeners)) wg.Add(len(listeners))
for _, l := range listeners { for _, l := range listeners {
go func(l *listener.T) { go func(l *listener.T) {
defer wg.Done() defer wg.Done()
l.Close() l.Close()
l.Drain()
if force {
l.DrainAll()
} else {
l.Drain()
}
}(l) }(l)
} }
wg.Wait()
for _, f := range posthooks {
f()
}
close(wait)
}
// Wait for all connections to gracefully shut down. This is commonly called at
// the bottom of the main() function to prevent the program from exiting
// prematurely.
func Wait() {
<-wait
} }

Loading…
Cancel
Save