veyron/lib/signals: fixing logic to remove STOP from signals passed to
ShutdownOnSignals. Add tests to ensure correctness.
Change-Id: Ia8cfbff9a39d2274d26f39519e76fdc9fa84f878
diff --git a/lib/signals/signals.go b/lib/signals/signals.go
index e9b88fe..75ed785 100644
--- a/lib/signals/signals.go
+++ b/lib/signals/signals.go
@@ -39,28 +39,28 @@
// 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
+ var signalsNoStop []os.Signal
+ for _, s := range signals {
+ switch s {
+ case STOP:
+ if !sawStop {
+ sawStop = true
+ if r := rt.R(); r != nil {
+ stopWaiter := make(chan string, 1)
+ r.WaitForStop(stopWaiter)
+ go func() {
+ for {
+ ch <- stopSignal(<-stopWaiter)
+ }
+ }()
+ }
}
- sawStop = true
- if r := rt.R(); r != nil {
- stopWaiter := make(chan string, 1)
- r.WaitForStop(stopWaiter)
- go func() {
- for {
- ch <- stopSignal(<-stopWaiter)
- }
- }()
- }
- } else {
- i++
+ default:
+ signalsNoStop = append(signalsNoStop, s)
}
}
- if len(signals) > 0 {
- signal.Notify(ch, signals...)
+ if len(signalsNoStop) > 0 {
+ signal.Notify(ch, signalsNoStop...)
}
// At least a buffer of length one so that we don't block on ret <- sig.
ret := make(chan os.Signal, 1)
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index d0a6b85..9303a79 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -21,6 +21,7 @@
func init() {
blackbox.CommandTable["handleDefaults"] = handleDefaults
blackbox.CommandTable["handleCustom"] = handleCustom
+ blackbox.CommandTable["handleCustomWithStop"] = handleCustomWithStop
blackbox.CommandTable["handleDefaultsIgnoreChan"] = handleDefaultsIgnoreChan
}
@@ -54,6 +55,10 @@
program(syscall.SIGABRT)
}
+func handleCustomWithStop([]string) {
+ program(STOP, syscall.SIGABRT, syscall.SIGHUP)
+}
+
func handleDefaultsIgnoreChan([]string) {
defer rt.Init().Shutdown()
closeStopLoop := make(chan struct{})
@@ -111,6 +116,20 @@
c.ExpectEOFAndWait()
}
+// TestCleanShutdownStopCustom verifies that sending a stop comamnd to a child
+// that handles stop command as part of a custom set of signals handled, causes
+// the child to shut down cleanly.
+func TestCleanShutdownStopCustom(t *testing.T) {
+ c := blackbox.HelperCommand(t, "handleCustomWithStop")
+ defer c.Cleanup()
+ c.Cmd.Start()
+ c.Expect("ready")
+ c.WriteLine("stop")
+ c.Expect(fmt.Sprintf("received signal %s", veyron2.LocalStop))
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+}
+
// TestStopNoHandler verifies that sending a stop command to a child that does
// not handle stop commands causes the child to exit immediately.
func TestStopNoHandler(t *testing.T) {
@@ -210,3 +229,33 @@
c.WriteLine("close")
c.ExpectEOFAndWait()
}
+
+// TestHandlerCustomSignalWithStop verifies that sending a custom stop signal
+// to a server that listens for that signal causes the server to shut down
+// cleanly, even when a STOP signal is also among the handled signals.
+func TestHandlerCustomSignalWithStop(t *testing.T) {
+ for _, signal := range([]syscall.Signal{syscall.SIGABRT, syscall.SIGHUP}) {
+ c := blackbox.HelperCommand(t, "handleCustomWithStop")
+ c.Cmd.Start()
+ c.Expect("ready")
+ checkSignalIsNotDefault(t, signal)
+ syscall.Kill(c.Cmd.Process.Pid, signal)
+ c.Expect(fmt.Sprintf("received signal %s", signal))
+ c.WriteLine("close")
+ c.ExpectEOFAndWait()
+ c.Cleanup()
+ }
+}
+
+// TestParseSignalsList verifies that ShutdownOnSignals correctly interprets
+// the input list of signals.
+func TestParseSignalsList(t *testing.T) {
+ list := []os.Signal{STOP, syscall.SIGTERM}
+ ShutdownOnSignals(list...)
+ if !isSignalInSet(syscall.SIGTERM, list) {
+ t.Errorf("signal %s not in signal set, as expected: %v", syscall.SIGTERM, list)
+ }
+ if !isSignalInSet(STOP, list) {
+ t.Errorf("signal %s not in signal set, as expected: %v", STOP, list)
+ }
+}
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index 91817f5..bcad19d 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -82,7 +82,7 @@
os.Exit(42) // This should not be reached.
}
-// TestNoWaiters verifies that the child process exits in the absense of any
+// TestNoWaiters verifies that the child process exits in the absence of any
// wait channel being registered with its runtime.
func TestNoWaiters(t *testing.T) {
c := blackbox.HelperCommand(t, "noWaiters")