blob: e9b88fe2669bfabd1d52c46dd2c2ddca55c9a046 [file] [log] [blame]
package signals
import (
"os"
"os/signal"
"syscall"
"veyron2/rt"
)
type stopSignal string
func (stopSignal) Signal() {}
func (s stopSignal) String() string { return string(s) }
const (
STOP = stopSignal("")
DoubleStopExitCode = 1
)
// defaultSignals returns a set of platform-specific signals that an application
// is encouraged to listen on.
func defaultSignals() []os.Signal {
return []os.Signal{syscall.SIGTERM, syscall.SIGINT, STOP}
}
// TODO(caprita): Rename this to Shutdown() and the package to shutdown since
// it's not just signals anymore.
// ShutdownOnSignals registers signal handlers for the specified signals, or, if
// none are specified, the default signals. The first signal received will be
// made available on the returned channel; upon receiving a second signal, the
// process will exit.
func ShutdownOnSignals(signals ...os.Signal) <-chan os.Signal {
if len(signals) == 0 {
signals = defaultSignals()
}
// At least a buffer of length two so that we don't drop the first two
// signals we get on account of the channel being full.
ch := make(chan os.Signal, 2)
sawStop := false
for i := 0; i < len(signals); {
if s := signals[i]; s == STOP {
signals = append(signals[:i], signals[i+1:]...)
if sawStop {
continue
}
sawStop = true
if r := rt.R(); r != nil {
stopWaiter := make(chan string, 1)
r.WaitForStop(stopWaiter)
go func() {
for {
ch <- stopSignal(<-stopWaiter)
}
}()
}
} else {
i++
}
}
if len(signals) > 0 {
signal.Notify(ch, signals...)
}
// At least a buffer of length one so that we don't block on ret <- sig.
ret := make(chan os.Signal, 1)
go func() {
// First signal received.
sig := <-ch
ret <- sig
// Wait for a second signal, and force an exit if the process is
// still executing cleanup code.
<-ch
os.Exit(DoubleStopExitCode)
}()
return ret
}