Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 1 | package signals |
| 2 | |
| 3 | import ( |
| 4 | "os" |
| 5 | "os/signal" |
| 6 | "syscall" |
| 7 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 8 | "v.io/core/veyron2" |
Suharsh Sivakumar | 946f64d | 2015-01-08 10:48:13 -0800 | [diff] [blame^] | 9 | "v.io/core/veyron2/context" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 10 | ) |
| 11 | |
| 12 | type stopSignal string |
| 13 | |
| 14 | func (stopSignal) Signal() {} |
| 15 | func (s stopSignal) String() string { return string(s) } |
| 16 | |
| 17 | const ( |
| 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. |
| 24 | func 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 Sivakumar | 946f64d | 2015-01-08 10:48:13 -0800 | [diff] [blame^] | 35 | func ShutdownOnSignals(ctx *context.T, signals ...os.Signal) <-chan os.Signal { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 36 | 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 Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 43 | var signalsNoStop []os.Signal |
| 44 | for _, s := range signals { |
| 45 | switch s { |
| 46 | case STOP: |
| 47 | if !sawStop { |
| 48 | sawStop = true |
Suharsh Sivakumar | 946f64d | 2015-01-08 10:48:13 -0800 | [diff] [blame^] | 49 | if ctx != nil { |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 50 | stopWaiter := make(chan string, 1) |
Suharsh Sivakumar | 946f64d | 2015-01-08 10:48:13 -0800 | [diff] [blame^] | 51 | veyron2.GetAppCycle(ctx).WaitForStop(stopWaiter) |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 52 | go func() { |
| 53 | for { |
| 54 | ch <- stopSignal(<-stopWaiter) |
| 55 | } |
| 56 | }() |
| 57 | } |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 58 | } |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 59 | default: |
| 60 | signalsNoStop = append(signalsNoStop, s) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 61 | } |
| 62 | } |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 63 | if len(signalsNoStop) > 0 { |
| 64 | signal.Notify(ch, signalsNoStop...) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 65 | } |
| 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 | } |