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 | |
Matt Rosencrantz | c7fecf1 | 2014-11-27 19:58:43 -0800 | [diff] [blame] | 8 | "veyron.io/veyron/veyron2" |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 9 | ) |
| 10 | |
| 11 | type stopSignal string |
| 12 | |
| 13 | func (stopSignal) Signal() {} |
| 14 | func (s stopSignal) String() string { return string(s) } |
| 15 | |
| 16 | const ( |
| 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. |
| 23 | func 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 Rosencrantz | c7fecf1 | 2014-11-27 19:58:43 -0800 | [diff] [blame] | 34 | func ShutdownOnSignals(runtime veyron2.Runtime, signals ...os.Signal) <-chan os.Signal { |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 35 | 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 Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 42 | var signalsNoStop []os.Signal |
| 43 | for _, s := range signals { |
| 44 | switch s { |
| 45 | case STOP: |
| 46 | if !sawStop { |
| 47 | sawStop = true |
Matt Rosencrantz | c7fecf1 | 2014-11-27 19:58:43 -0800 | [diff] [blame] | 48 | if runtime != nil { |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 49 | stopWaiter := make(chan string, 1) |
Matt Rosencrantz | c7fecf1 | 2014-11-27 19:58:43 -0800 | [diff] [blame] | 50 | runtime.AppCycle().WaitForStop(stopWaiter) |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 51 | go func() { |
| 52 | for { |
| 53 | ch <- stopSignal(<-stopWaiter) |
| 54 | } |
| 55 | }() |
| 56 | } |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 57 | } |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 58 | default: |
| 59 | signalsNoStop = append(signalsNoStop, s) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 60 | } |
| 61 | } |
Bogdan Caprita | 1002ba4 | 2014-06-06 19:24:40 -0700 | [diff] [blame] | 62 | if len(signalsNoStop) > 0 { |
| 63 | signal.Notify(ch, signalsNoStop...) |
Jiri Simsa | 5293dcb | 2014-05-10 09:56:38 -0700 | [diff] [blame] | 64 | } |
| 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 | } |