github.com/ActiveState/go@v0.0.0-20170614201249-0b81c023a722/src/os/signal/signal_test.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris 6 7 package signal_test 8 9 import ( 10 "flag" 11 "io/ioutil" 12 "os" 13 "os/exec" 14 . "os/signal" 15 "runtime" 16 "strconv" 17 "syscall" 18 "testing" 19 "time" 20 ) 21 22 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { 23 select { 24 case s := <-c: 25 if s != sig { 26 t.Fatalf("signal was %v, want %v", s, sig) 27 } 28 case <-time.After(1 * time.Second): 29 t.Fatalf("timeout waiting for %v", sig) 30 } 31 } 32 33 // Test that basic signal handling works. 34 func TestSignal(t *testing.T) { 35 // Ask for SIGHUP 36 c := make(chan os.Signal, 1) 37 Notify(c, syscall.SIGHUP) 38 defer Stop(c) 39 40 // Send this process a SIGHUP 41 t.Logf("sighup...") 42 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 43 waitSig(t, c, syscall.SIGHUP) 44 45 // Ask for everything we can get. 46 c1 := make(chan os.Signal, 1) 47 Notify(c1) 48 49 // Send this process a SIGWINCH 50 t.Logf("sigwinch...") 51 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 52 waitSig(t, c1, syscall.SIGWINCH) 53 54 // Send two more SIGHUPs, to make sure that 55 // they get delivered on c1 and that not reading 56 // from c does not block everything. 57 t.Logf("sighup...") 58 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 59 waitSig(t, c1, syscall.SIGHUP) 60 t.Logf("sighup...") 61 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 62 waitSig(t, c1, syscall.SIGHUP) 63 64 // The first SIGHUP should be waiting for us on c. 65 waitSig(t, c, syscall.SIGHUP) 66 } 67 68 func TestStress(t *testing.T) { 69 dur := 3 * time.Second 70 if testing.Short() { 71 dur = 100 * time.Millisecond 72 } 73 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 74 done := make(chan bool) 75 finished := make(chan bool) 76 go func() { 77 sig := make(chan os.Signal, 1) 78 Notify(sig, syscall.SIGUSR1) 79 defer Stop(sig) 80 Loop: 81 for { 82 select { 83 case <-sig: 84 case <-done: 85 break Loop 86 } 87 } 88 finished <- true 89 }() 90 go func() { 91 Loop: 92 for { 93 select { 94 case <-done: 95 break Loop 96 default: 97 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 98 runtime.Gosched() 99 } 100 } 101 finished <- true 102 }() 103 time.Sleep(dur) 104 close(done) 105 <-finished 106 <-finished 107 // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip 108 // into subsequent TestSignal() causing failure. 109 // Sleep for a while to reduce the possibility of the failure. 110 time.Sleep(10 * time.Millisecond) 111 } 112 113 func testCancel(t *testing.T, ignore bool) { 114 // Send SIGWINCH. By default this signal should be ignored. 115 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 116 time.Sleep(100 * time.Millisecond) 117 118 // Ask to be notified on c1 when a SIGWINCH is received. 119 c1 := make(chan os.Signal, 1) 120 Notify(c1, syscall.SIGWINCH) 121 defer Stop(c1) 122 123 // Ask to be notified on c2 when a SIGHUP is received. 124 c2 := make(chan os.Signal, 1) 125 Notify(c2, syscall.SIGHUP) 126 defer Stop(c2) 127 128 // Send this process a SIGWINCH and wait for notification on c1. 129 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 130 waitSig(t, c1, syscall.SIGWINCH) 131 132 // Send this process a SIGHUP and wait for notification on c2. 133 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 134 waitSig(t, c2, syscall.SIGHUP) 135 136 // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP. 137 if ignore { 138 Ignore(syscall.SIGWINCH, syscall.SIGHUP) 139 } else { 140 Reset(syscall.SIGWINCH, syscall.SIGHUP) 141 } 142 143 // At this point we do not expect any further signals on c1. 144 // However, it is just barely possible that the initial SIGWINCH 145 // at the start of this function was delivered after we called 146 // Notify on c1. In that case the waitSig for SIGWINCH may have 147 // picked up that initial SIGWINCH, and the second SIGWINCH may 148 // then have been delivered on the channel. This sequence of events 149 // may have caused issue 15661. 150 // So, read any possible signal from the channel now. 151 select { 152 case <-c1: 153 default: 154 } 155 156 // Send this process a SIGWINCH. It should be ignored. 157 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 158 159 // If ignoring, Send this process a SIGHUP. It should be ignored. 160 if ignore { 161 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 162 } 163 164 select { 165 case s := <-c1: 166 t.Fatalf("unexpected signal %v", s) 167 case <-time.After(100 * time.Millisecond): 168 // nothing to read - good 169 } 170 171 select { 172 case s := <-c2: 173 t.Fatalf("unexpected signal %v", s) 174 case <-time.After(100 * time.Millisecond): 175 // nothing to read - good 176 } 177 178 // Reset the signal handlers for all signals. 179 Reset() 180 } 181 182 // Test that Reset cancels registration for listed signals on all channels. 183 func TestReset(t *testing.T) { 184 testCancel(t, false) 185 } 186 187 // Test that Ignore cancels registration for listed signals on all channels. 188 func TestIgnore(t *testing.T) { 189 testCancel(t, true) 190 } 191 192 var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") 193 194 // Test that Stop cancels the channel's registrations. 195 func TestStop(t *testing.T) { 196 sigs := []syscall.Signal{ 197 syscall.SIGWINCH, 198 syscall.SIGHUP, 199 syscall.SIGUSR1, 200 } 201 202 for _, sig := range sigs { 203 // Send the signal. 204 // If it's SIGWINCH, we should not see it. 205 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 206 if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) { 207 syscall.Kill(syscall.Getpid(), sig) 208 } 209 time.Sleep(100 * time.Millisecond) 210 211 // Ask for signal 212 c := make(chan os.Signal, 1) 213 Notify(c, sig) 214 defer Stop(c) 215 216 // Send this process that signal 217 syscall.Kill(syscall.Getpid(), sig) 218 waitSig(t, c, sig) 219 220 Stop(c) 221 select { 222 case s := <-c: 223 t.Fatalf("unexpected signal %v", s) 224 case <-time.After(100 * time.Millisecond): 225 // nothing to read - good 226 } 227 228 // Send the signal. 229 // If it's SIGWINCH, we should not see it. 230 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 231 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { 232 syscall.Kill(syscall.Getpid(), sig) 233 } 234 235 select { 236 case s := <-c: 237 t.Fatalf("unexpected signal %v", s) 238 case <-time.After(100 * time.Millisecond): 239 // nothing to read - good 240 } 241 } 242 } 243 244 // Test that when run under nohup, an uncaught SIGHUP does not kill the program, 245 // but a 246 func TestNohup(t *testing.T) { 247 // Ugly: ask for SIGHUP so that child will not have no-hup set 248 // even if test is running under nohup environment. 249 // We have no intention of reading from c. 250 c := make(chan os.Signal, 1) 251 Notify(c, syscall.SIGHUP) 252 253 // When run without nohup, the test should crash on an uncaught SIGHUP. 254 // When run under nohup, the test should ignore uncaught SIGHUPs, 255 // because the runtime is not supposed to be listening for them. 256 // Either way, TestStop should still be able to catch them when it wants them 257 // and then when it stops wanting them, the original behavior should resume. 258 // 259 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. 260 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. 261 // 262 // Both should fail without nohup and succeed with nohup. 263 264 for i := 1; i <= 2; i++ { 265 out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 266 if err == nil { 267 t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) 268 } 269 } 270 271 Stop(c) 272 273 // Skip the nohup test below when running in tmux on darwin, since nohup 274 // doesn't work correctly there. See issue #5135. 275 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" { 276 t.Skip("Skipping nohup test due to running in tmux on darwin") 277 } 278 279 // Again, this time with nohup, assuming we can find it. 280 _, err := os.Stat("/usr/bin/nohup") 281 if err != nil { 282 t.Skip("cannot find nohup; skipping second half of test") 283 } 284 285 for i := 1; i <= 2; i++ { 286 os.Remove("nohup.out") 287 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 288 289 data, _ := ioutil.ReadFile("nohup.out") 290 os.Remove("nohup.out") 291 if err != nil { 292 t.Fatalf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", i, err, out, data) 293 } 294 } 295 } 296 297 // Test that SIGCONT works (issue 8953). 298 func TestSIGCONT(t *testing.T) { 299 c := make(chan os.Signal, 1) 300 Notify(c, syscall.SIGCONT) 301 defer Stop(c) 302 syscall.Kill(syscall.Getpid(), syscall.SIGCONT) 303 waitSig(t, c, syscall.SIGCONT) 304 }