aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/zenazn/goji/graceful/signal.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zenazn/goji/graceful/signal.go')
-rw-r--r--vendor/github.com/zenazn/goji/graceful/signal.go197
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)
+ }
+}