blob: 35d29658950e362b2649a32492dffe3af243de02 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package signals
2
3import (
4 "os"
5 "os/signal"
6 "syscall"
7
Matt Rosencrantzc7fecf12014-11-27 19:58:43 -08008 "veyron.io/veyron/veyron2"
Jiri Simsa5293dcb2014-05-10 09:56:38 -07009)
10
11type stopSignal string
12
13func (stopSignal) Signal() {}
14func (s stopSignal) String() string { return string(s) }
15
16const (
17 STOP = stopSignal("")
18 DoubleStopExitCode = 1
19)
20
21// defaultSignals returns a set of platform-specific signals that an application
22// is encouraged to listen on.
23func defaultSignals() []os.Signal {
24 return []os.Signal{syscall.SIGTERM, syscall.SIGINT, STOP}
25}
26
27// TODO(caprita): Rename this to Shutdown() and the package to shutdown since
28// it's not just signals anymore.
29
30// ShutdownOnSignals registers signal handlers for the specified signals, or, if
31// none are specified, the default signals. The first signal received will be
32// made available on the returned channel; upon receiving a second signal, the
33// process will exit.
Matt Rosencrantzc7fecf12014-11-27 19:58:43 -080034func ShutdownOnSignals(runtime veyron2.Runtime, signals ...os.Signal) <-chan os.Signal {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070035 if len(signals) == 0 {
36 signals = defaultSignals()
37 }
38 // At least a buffer of length two so that we don't drop the first two
39 // signals we get on account of the channel being full.
40 ch := make(chan os.Signal, 2)
41 sawStop := false
Bogdan Caprita1002ba42014-06-06 19:24:40 -070042 var signalsNoStop []os.Signal
43 for _, s := range signals {
44 switch s {
45 case STOP:
46 if !sawStop {
47 sawStop = true
Matt Rosencrantzc7fecf12014-11-27 19:58:43 -080048 if runtime != nil {
Bogdan Caprita1002ba42014-06-06 19:24:40 -070049 stopWaiter := make(chan string, 1)
Matt Rosencrantzc7fecf12014-11-27 19:58:43 -080050 runtime.AppCycle().WaitForStop(stopWaiter)
Bogdan Caprita1002ba42014-06-06 19:24:40 -070051 go func() {
52 for {
53 ch <- stopSignal(<-stopWaiter)
54 }
55 }()
56 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -070057 }
Bogdan Caprita1002ba42014-06-06 19:24:40 -070058 default:
59 signalsNoStop = append(signalsNoStop, s)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070060 }
61 }
Bogdan Caprita1002ba42014-06-06 19:24:40 -070062 if len(signalsNoStop) > 0 {
63 signal.Notify(ch, signalsNoStop...)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070064 }
65 // At least a buffer of length one so that we don't block on ret <- sig.
66 ret := make(chan os.Signal, 1)
67 go func() {
68 // First signal received.
69 sig := <-ch
70 ret <- sig
71 // Wait for a second signal, and force an exit if the process is
72 // still executing cleanup code.
73 <-ch
74 os.Exit(DoubleStopExitCode)
75 }()
76 return ret
77}