blob: e2d64a66139b4d33ce77b878d78bfae1661e26ff [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package signals
2
3import (
4 "os"
5 "os/signal"
6 "syscall"
7
Jiri Simsa764efb72014-12-25 20:57:03 -08008 "v.io/core/veyron2"
Suharsh Sivakumar946f64d2015-01-08 10:48:13 -08009 "v.io/core/veyron2/context"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070010)
11
12type stopSignal string
13
14func (stopSignal) Signal() {}
15func (s stopSignal) String() string { return string(s) }
16
17const (
18 STOP = stopSignal("")
19 DoubleStopExitCode = 1
20)
21
22// defaultSignals returns a set of platform-specific signals that an application
23// is encouraged to listen on.
24func defaultSignals() []os.Signal {
25 return []os.Signal{syscall.SIGTERM, syscall.SIGINT, STOP}
26}
27
28// TODO(caprita): Rename this to Shutdown() and the package to shutdown since
29// it's not just signals anymore.
30
31// ShutdownOnSignals registers signal handlers for the specified signals, or, if
32// none are specified, the default signals. The first signal received will be
33// made available on the returned channel; upon receiving a second signal, the
34// process will exit.
Suharsh Sivakumar946f64d2015-01-08 10:48:13 -080035func ShutdownOnSignals(ctx *context.T, signals ...os.Signal) <-chan os.Signal {
Jiri Simsa5293dcb2014-05-10 09:56:38 -070036 if len(signals) == 0 {
37 signals = defaultSignals()
38 }
39 // At least a buffer of length two so that we don't drop the first two
40 // signals we get on account of the channel being full.
41 ch := make(chan os.Signal, 2)
42 sawStop := false
Bogdan Caprita1002ba42014-06-06 19:24:40 -070043 var signalsNoStop []os.Signal
44 for _, s := range signals {
45 switch s {
46 case STOP:
47 if !sawStop {
48 sawStop = true
Suharsh Sivakumar946f64d2015-01-08 10:48:13 -080049 if ctx != nil {
Bogdan Caprita1002ba42014-06-06 19:24:40 -070050 stopWaiter := make(chan string, 1)
Suharsh Sivakumar946f64d2015-01-08 10:48:13 -080051 veyron2.GetAppCycle(ctx).WaitForStop(stopWaiter)
Bogdan Caprita1002ba42014-06-06 19:24:40 -070052 go func() {
53 for {
54 ch <- stopSignal(<-stopWaiter)
55 }
56 }()
57 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -070058 }
Bogdan Caprita1002ba42014-06-06 19:24:40 -070059 default:
60 signalsNoStop = append(signalsNoStop, s)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070061 }
62 }
Bogdan Caprita1002ba42014-06-06 19:24:40 -070063 if len(signalsNoStop) > 0 {
64 signal.Notify(ch, signalsNoStop...)
Jiri Simsa5293dcb2014-05-10 09:56:38 -070065 }
66 // At least a buffer of length one so that we don't block on ret <- sig.
67 ret := make(chan os.Signal, 1)
68 go func() {
69 // First signal received.
70 sig := <-ch
71 ret <- sig
72 // Wait for a second signal, and force an exit if the process is
73 // still executing cleanup code.
74 <-ch
75 os.Exit(DoubleStopExitCode)
76 }()
77 return ret
78}