diff options
Diffstat (limited to 'vendor/github.com/zenazn/goji/graceful/signal.go')
| -rw-r--r-- | vendor/github.com/zenazn/goji/graceful/signal.go | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/vendor/github.com/zenazn/goji/graceful/signal.go b/vendor/github.com/zenazn/goji/graceful/signal.go new file mode 100644 index 0000000..60612b8 --- /dev/null +++ b/vendor/github.com/zenazn/goji/graceful/signal.go @@ -0,0 +1,197 @@ +package graceful + +import ( + "os" + "os/signal" + "sync" + "sync/atomic" + "time" + + "github.com/zenazn/goji/graceful/listener" +) + +var mu sync.Mutex // protects everything that follows +var listeners = make([]*listener.T, 0) +var prehooks = make([]func(), 0) +var posthooks = make([]func(), 0) +var closing int32 +var doubleKick, timeout time.Duration + +var wait = make(chan struct{}) +var stdSignals = []os.Signal{os.Interrupt} +var sigchan = make(chan os.Signal, 1) + +// HandleSignals installs signal handlers for a set of standard signals. By +// default, this set only includes keyboard interrupts, however when the package +// detects that it is running under Einhorn, a SIGUSR2 handler is installed as +// well. +func HandleSignals() { + AddSignal(stdSignals...) +} + +// AddSignal adds the given signal to the set of signals that trigger a graceful +// shutdown. +func AddSignal(sig ...os.Signal) { + signal.Notify(sigchan, sig...) +} + +// ResetSignals resets the list of signals that trigger a graceful shutdown. +func ResetSignals() { + signal.Stop(sigchan) +} + +// 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, +// from a single goroutine. +func PreHook(f func()) { + mu.Lock() + defer mu.Unlock() + + prehooks = append(prehooks, f) +} + +// PostHook registers a function to be called after all of this package's normal +// shutdown actions. All listeners will be called in the order they were added, +// from a single goroutine, and are guaranteed to be called after all listening +// connections have been closed, but before Wait() returns. +// +// If you've Hijacked any connections that must be gracefully shut down in some +// other way (since this library disowns all hijacked connections), it's +// reasonable to use a PostHook to signal and wait for them. +func PostHook(f func()) { + mu.Lock() + defer mu.Unlock() + + posthooks = append(posthooks, f) +} + +// Shutdown manually triggers a shutdown from your application. Like Wait, +// blocks until all connections have gracefully shut down. +func Shutdown() { + shutdown(false) +} + +// 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) +} + +// DoubleKickWindow sets the length of the window during which two back-to-back +// signals are treated as an especially urgent or forceful request to exit +// (i.e., ShutdownNow instead of Shutdown). Signals delivered more than this +// duration apart are treated as separate requests to exit gracefully as usual. +// +// Setting DoubleKickWindow to 0 disables the feature. +func DoubleKickWindow(d time.Duration) { + if d < 0 { + return + } + mu.Lock() + defer mu.Unlock() + + doubleKick = d +} + +// Timeout sets the maximum amount of time package graceful will wait for +// connections to gracefully shut down after receiving a signal. After this +// timeout, connections will be forcefully shut down (similar to calling +// ShutdownNow). +// +// Setting Timeout to 0 disables the feature. +func Timeout(d time.Duration) { + if d < 0 { + return + } + mu.Lock() + defer mu.Unlock() + + timeout = d +} + +// 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() { + var last time.Time + for { + <-sigchan + now := time.Now() + mu.Lock() + force := doubleKick != 0 && now.Sub(last) < doubleKick + if t := timeout; t != 0 && !force { + go func() { + time.Sleep(t) + shutdown(true) + }() + } + mu.Unlock() + go shutdown(force) + last = now + } +} + +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) + + var wg sync.WaitGroup + defer wg.Wait() + + mu.Lock() + defer mu.Unlock() + wg.Add(len(listeners)) + + for _, l := range listeners { + go func(l *listener.T) { + defer wg.Done() + l.Close() + if force { + l.DrainAll() + } else { + l.Drain() + } + }(l) + } +} |
