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 aix darwin dragonfly freebsd hurd linux netbsd openbsd solaris 6 7package signal 8 9import ( 10 "bytes" 11 "flag" 12 "fmt" 13 "internal/testenv" 14 "io/ioutil" 15 "os" 16 "os/exec" 17 "runtime" 18 "strconv" 19 "sync" 20 "syscall" 21 "testing" 22 "time" 23) 24 25var testDeadline time.Time 26 27func TestMain(m *testing.M) { 28 flag.Parse() 29 30 // TODO(golang.org/issue/28135): Remove this setup and use t.Deadline instead. 31 timeoutFlag := flag.Lookup("test.timeout") 32 if timeoutFlag != nil { 33 if d := timeoutFlag.Value.(flag.Getter).Get().(time.Duration); d != 0 { 34 testDeadline = time.Now().Add(d) 35 } 36 } 37 38 os.Exit(m.Run()) 39} 40 41func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { 42 waitSig1(t, c, sig, false) 43} 44func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) { 45 waitSig1(t, c, sig, true) 46} 47 48func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) { 49 // Sleep multiple times to give the kernel more tries to 50 // deliver the signal. 51 for i := 0; i < 10; i++ { 52 select { 53 case s := <-c: 54 // If the caller notified for all signals on 55 // c, filter out SIGURG, which is used for 56 // runtime preemption and can come at 57 // unpredictable times. 58 if all && s == syscall.SIGURG { 59 continue 60 } 61 if s != sig { 62 t.Fatalf("signal was %v, want %v", s, sig) 63 } 64 return 65 66 case <-time.After(100 * time.Millisecond): 67 } 68 } 69 t.Fatalf("timeout waiting for %v", sig) 70} 71 72// Test that basic signal handling works. 73func TestSignal(t *testing.T) { 74 // Ask for SIGHUP 75 c := make(chan os.Signal, 1) 76 Notify(c, syscall.SIGHUP) 77 defer Stop(c) 78 79 // Send this process a SIGHUP 80 t.Logf("sighup...") 81 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 82 waitSig(t, c, syscall.SIGHUP) 83 84 // Ask for everything we can get. The buffer size has to be 85 // more than 1, since the runtime might send SIGURG signals. 86 // Using 10 is arbitrary. 87 c1 := make(chan os.Signal, 10) 88 Notify(c1) 89 90 // Send this process a SIGWINCH 91 t.Logf("sigwinch...") 92 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 93 waitSigAll(t, c1, syscall.SIGWINCH) 94 95 // Send two more SIGHUPs, to make sure that 96 // they get delivered on c1 and that not reading 97 // from c does not block everything. 98 t.Logf("sighup...") 99 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 100 waitSigAll(t, c1, syscall.SIGHUP) 101 t.Logf("sighup...") 102 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 103 waitSigAll(t, c1, syscall.SIGHUP) 104 105 // The first SIGHUP should be waiting for us on c. 106 waitSig(t, c, syscall.SIGHUP) 107} 108 109func TestStress(t *testing.T) { 110 dur := 3 * time.Second 111 if testing.Short() { 112 dur = 100 * time.Millisecond 113 } 114 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 115 done := make(chan bool) 116 finished := make(chan bool) 117 go func() { 118 sig := make(chan os.Signal, 1) 119 Notify(sig, syscall.SIGUSR1) 120 defer Stop(sig) 121 Loop: 122 for { 123 select { 124 case <-sig: 125 case <-done: 126 break Loop 127 } 128 } 129 finished <- true 130 }() 131 go func() { 132 Loop: 133 for { 134 select { 135 case <-done: 136 break Loop 137 default: 138 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 139 runtime.Gosched() 140 } 141 } 142 finished <- true 143 }() 144 time.Sleep(dur) 145 close(done) 146 <-finished 147 <-finished 148 // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip 149 // into subsequent TestSignal() causing failure. 150 // Sleep for a while to reduce the possibility of the failure. 151 time.Sleep(10 * time.Millisecond) 152} 153 154func testCancel(t *testing.T, ignore bool) { 155 // Send SIGWINCH. By default this signal should be ignored. 156 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 157 time.Sleep(100 * time.Millisecond) 158 159 // Ask to be notified on c1 when a SIGWINCH is received. 160 c1 := make(chan os.Signal, 1) 161 Notify(c1, syscall.SIGWINCH) 162 defer Stop(c1) 163 164 // Ask to be notified on c2 when a SIGHUP is received. 165 c2 := make(chan os.Signal, 1) 166 Notify(c2, syscall.SIGHUP) 167 defer Stop(c2) 168 169 // Send this process a SIGWINCH and wait for notification on c1. 170 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 171 waitSig(t, c1, syscall.SIGWINCH) 172 173 // Send this process a SIGHUP and wait for notification on c2. 174 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 175 waitSig(t, c2, syscall.SIGHUP) 176 177 // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP. 178 if ignore { 179 Ignore(syscall.SIGWINCH, syscall.SIGHUP) 180 } else { 181 Reset(syscall.SIGWINCH, syscall.SIGHUP) 182 } 183 184 // At this point we do not expect any further signals on c1. 185 // However, it is just barely possible that the initial SIGWINCH 186 // at the start of this function was delivered after we called 187 // Notify on c1. In that case the waitSig for SIGWINCH may have 188 // picked up that initial SIGWINCH, and the second SIGWINCH may 189 // then have been delivered on the channel. This sequence of events 190 // may have caused issue 15661. 191 // So, read any possible signal from the channel now. 192 select { 193 case <-c1: 194 default: 195 } 196 197 // Send this process a SIGWINCH. It should be ignored. 198 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 199 200 // If ignoring, Send this process a SIGHUP. It should be ignored. 201 if ignore { 202 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 203 } 204 205 select { 206 case s := <-c1: 207 t.Fatalf("unexpected signal %v", s) 208 case <-time.After(100 * time.Millisecond): 209 // nothing to read - good 210 } 211 212 select { 213 case s := <-c2: 214 t.Fatalf("unexpected signal %v", s) 215 case <-time.After(100 * time.Millisecond): 216 // nothing to read - good 217 } 218 219 // Reset the signal handlers for all signals. 220 Reset() 221} 222 223// Test that Reset cancels registration for listed signals on all channels. 224func TestReset(t *testing.T) { 225 testCancel(t, false) 226} 227 228// Test that Ignore cancels registration for listed signals on all channels. 229func TestIgnore(t *testing.T) { 230 testCancel(t, true) 231} 232 233// Test that Ignored correctly detects changes to the ignored status of a signal. 234func TestIgnored(t *testing.T) { 235 // Ask to be notified on SIGWINCH. 236 c := make(chan os.Signal, 1) 237 Notify(c, syscall.SIGWINCH) 238 239 // If we're being notified, then the signal should not be ignored. 240 if Ignored(syscall.SIGWINCH) { 241 t.Errorf("expected SIGWINCH to not be ignored.") 242 } 243 Stop(c) 244 Ignore(syscall.SIGWINCH) 245 246 // We're no longer paying attention to this signal. 247 if !Ignored(syscall.SIGWINCH) { 248 t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.") 249 } 250 251 Reset() 252} 253 254var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.") 255 256// Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup. 257func TestDetectNohup(t *testing.T) { 258 if *checkSighupIgnored { 259 if !Ignored(syscall.SIGHUP) { 260 t.Fatal("SIGHUP is not ignored.") 261 } else { 262 t.Log("SIGHUP is ignored.") 263 } 264 } else { 265 defer Reset() 266 // Ugly: ask for SIGHUP so that child will not have no-hup set 267 // even if test is running under nohup environment. 268 // We have no intention of reading from c. 269 c := make(chan os.Signal, 1) 270 Notify(c, syscall.SIGHUP) 271 if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil { 272 t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out) 273 } 274 Stop(c) 275 // Again, this time with nohup, assuming we can find it. 276 _, err := os.Stat("/usr/bin/nohup") 277 if err != nil { 278 t.Skip("cannot find nohup; skipping second half of test") 279 } 280 Ignore(syscall.SIGHUP) 281 os.Remove("nohup.out") 282 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput() 283 284 data, _ := ioutil.ReadFile("nohup.out") 285 os.Remove("nohup.out") 286 if err != nil { 287 t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data) 288 } 289 } 290} 291 292var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") 293 294// Test that Stop cancels the channel's registrations. 295func TestStop(t *testing.T) { 296 sigs := []syscall.Signal{ 297 syscall.SIGWINCH, 298 syscall.SIGHUP, 299 syscall.SIGUSR1, 300 } 301 302 for _, sig := range sigs { 303 // Send the signal. 304 // If it's SIGWINCH, we should not see it. 305 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 306 if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) { 307 syscall.Kill(syscall.Getpid(), sig) 308 } 309 310 // The kernel will deliver a signal as a thread returns 311 // from a syscall. If the only active thread is sleeping, 312 // and the system is busy, the kernel may not get around 313 // to waking up a thread to catch the signal. 314 // We try splitting up the sleep to give the kernel 315 // another chance to deliver the signal. 316 time.Sleep(50 * time.Millisecond) 317 time.Sleep(50 * time.Millisecond) 318 319 // Ask for signal 320 c := make(chan os.Signal, 1) 321 Notify(c, sig) 322 defer Stop(c) 323 324 // Send this process that signal 325 syscall.Kill(syscall.Getpid(), sig) 326 waitSig(t, c, sig) 327 328 Stop(c) 329 time.Sleep(50 * time.Millisecond) 330 select { 331 case s := <-c: 332 t.Fatalf("unexpected signal %v", s) 333 case <-time.After(50 * time.Millisecond): 334 // nothing to read - good 335 } 336 337 // Send the signal. 338 // If it's SIGWINCH, we should not see it. 339 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 340 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { 341 syscall.Kill(syscall.Getpid(), sig) 342 } 343 344 time.Sleep(50 * time.Millisecond) 345 select { 346 case s := <-c: 347 t.Fatalf("unexpected signal %v", s) 348 case <-time.After(50 * time.Millisecond): 349 // nothing to read - good 350 } 351 } 352} 353 354// Test that when run under nohup, an uncaught SIGHUP does not kill the program, 355// but a 356func TestNohup(t *testing.T) { 357 // Ugly: ask for SIGHUP so that child will not have no-hup set 358 // even if test is running under nohup environment. 359 // We have no intention of reading from c. 360 c := make(chan os.Signal, 1) 361 Notify(c, syscall.SIGHUP) 362 363 // When run without nohup, the test should crash on an uncaught SIGHUP. 364 // When run under nohup, the test should ignore uncaught SIGHUPs, 365 // because the runtime is not supposed to be listening for them. 366 // Either way, TestStop should still be able to catch them when it wants them 367 // and then when it stops wanting them, the original behavior should resume. 368 // 369 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. 370 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. 371 // 372 // Both should fail without nohup and succeed with nohup. 373 374 for i := 1; i <= 2; i++ { 375 out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 376 if err == nil { 377 t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) 378 } 379 } 380 381 Stop(c) 382 383 // Skip the nohup test below when running in tmux on darwin, since nohup 384 // doesn't work correctly there. See issue #5135. 385 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" { 386 t.Skip("Skipping nohup test due to running in tmux on darwin") 387 } 388 389 // Again, this time with nohup, assuming we can find it. 390 _, err := os.Stat("/usr/bin/nohup") 391 if err != nil { 392 t.Skip("cannot find nohup; skipping second half of test") 393 } 394 395 for i := 1; i <= 2; i++ { 396 os.Remove("nohup.out") 397 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput() 398 399 data, _ := ioutil.ReadFile("nohup.out") 400 os.Remove("nohup.out") 401 if err != nil { 402 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) 403 } 404 } 405} 406 407// Test that SIGCONT works (issue 8953). 408func TestSIGCONT(t *testing.T) { 409 c := make(chan os.Signal, 1) 410 Notify(c, syscall.SIGCONT) 411 defer Stop(c) 412 syscall.Kill(syscall.Getpid(), syscall.SIGCONT) 413 waitSig(t, c, syscall.SIGCONT) 414} 415 416// Test race between stopping and receiving a signal (issue 14571). 417func TestAtomicStop(t *testing.T) { 418 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" { 419 atomicStopTestProgram() 420 t.Fatal("atomicStopTestProgram returned") 421 } 422 423 testenv.MustHaveExec(t) 424 425 // Call Notify for SIGINT before starting the child process. 426 // That ensures that SIGINT is not ignored for the child. 427 // This is necessary because if SIGINT is ignored when a 428 // Go program starts, then it remains ignored, and closing 429 // the last notification channel for SIGINT will switch it 430 // back to being ignored. In that case the assumption of 431 // atomicStopTestProgram, that it will either die from SIGINT 432 // or have it be reported, breaks down, as there is a third 433 // option: SIGINT might be ignored. 434 cs := make(chan os.Signal, 1) 435 Notify(cs, syscall.SIGINT) 436 defer Stop(cs) 437 438 const execs = 10 439 for i := 0; i < execs; i++ { 440 timeout := "0" 441 if !testDeadline.IsZero() { 442 timeout = testDeadline.Sub(time.Now()).String() 443 } 444 cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout) 445 cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1") 446 out, err := cmd.CombinedOutput() 447 if err == nil { 448 if len(out) > 0 { 449 t.Logf("iteration %d: output %s", i, out) 450 } 451 } else { 452 t.Logf("iteration %d: exit status %q: output: %s", i, err, out) 453 } 454 455 lost := bytes.Contains(out, []byte("lost signal")) 456 if lost { 457 t.Errorf("iteration %d: lost signal", i) 458 } 459 460 // The program should either die due to SIGINT, 461 // or exit with success without printing "lost signal". 462 if err == nil { 463 if len(out) > 0 && !lost { 464 t.Errorf("iteration %d: unexpected output", i) 465 } 466 } else { 467 if ee, ok := err.(*exec.ExitError); !ok { 468 t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err) 469 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 470 t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys()) 471 } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT { 472 t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee) 473 } 474 } 475 } 476} 477 478// atomicStopTestProgram is run in a subprocess by TestAtomicStop. 479// It tries to trigger a signal delivery race. This function should 480// either catch a signal or die from it. 481func atomicStopTestProgram() { 482 // This test won't work if SIGINT is ignored here. 483 if Ignored(syscall.SIGINT) { 484 fmt.Println("SIGINT is ignored") 485 os.Exit(1) 486 } 487 488 const tries = 10 489 490 timeout := 2 * time.Second 491 if !testDeadline.IsZero() { 492 // Give each try an equal slice of the deadline, with one slice to spare for 493 // cleanup. 494 timeout = testDeadline.Sub(time.Now()) / (tries + 1) 495 } 496 497 pid := syscall.Getpid() 498 printed := false 499 for i := 0; i < tries; i++ { 500 cs := make(chan os.Signal, 1) 501 Notify(cs, syscall.SIGINT) 502 503 var wg sync.WaitGroup 504 wg.Add(1) 505 go func() { 506 defer wg.Done() 507 Stop(cs) 508 }() 509 510 syscall.Kill(pid, syscall.SIGINT) 511 512 // At this point we should either die from SIGINT or 513 // get a notification on cs. If neither happens, we 514 // dropped the signal. It is given 2 seconds to 515 // deliver, as needed for gccgo on some loaded test systems. 516 517 select { 518 case <-cs: 519 case <-time.After(timeout): 520 if !printed { 521 fmt.Print("lost signal on tries:") 522 printed = true 523 } 524 fmt.Printf(" %d", i) 525 } 526 527 wg.Wait() 528 } 529 if printed { 530 fmt.Print("\n") 531 } 532 533 os.Exit(0) 534} 535 536func TestTime(t *testing.T) { 537 // Test that signal works fine when we are in a call to get time, 538 // which on some platforms is using VDSO. See issue #34391. 539 dur := 3 * time.Second 540 if testing.Short() { 541 dur = 100 * time.Millisecond 542 } 543 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 544 done := make(chan bool) 545 finished := make(chan bool) 546 go func() { 547 sig := make(chan os.Signal, 1) 548 Notify(sig, syscall.SIGUSR1) 549 defer Stop(sig) 550 Loop: 551 for { 552 select { 553 case <-sig: 554 case <-done: 555 break Loop 556 } 557 } 558 finished <- true 559 }() 560 go func() { 561 Loop: 562 for { 563 select { 564 case <-done: 565 break Loop 566 default: 567 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 568 runtime.Gosched() 569 } 570 } 571 finished <- true 572 }() 573 t0 := time.Now() 574 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() { 575 } // hammering on getting time 576 close(done) 577 <-finished 578 <-finished 579 // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip 580 // into subsequent TestSignal() causing failure. 581 // Sleep for a while to reduce the possibility of the failure. 582 time.Sleep(10 * time.Millisecond) 583} 584