github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/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 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 7 8 package signal 9 10 import ( 11 "bytes" 12 "context" 13 "flag" 14 "fmt" 15 "github.com/comwrg/go/src/internal/testenv" 16 "os" 17 "os/exec" 18 "runtime" 19 "runtime/trace" 20 "strconv" 21 "strings" 22 "sync" 23 "syscall" 24 "testing" 25 "time" 26 ) 27 28 // settleTime is an upper bound on how long we expect signals to take to be 29 // delivered. Lower values make the test faster, but also flakier — especially 30 // on heavily loaded systems. 31 // 32 // The current value is set based on flakes observed in the Go builders. 33 var settleTime = 100 * time.Millisecond 34 35 // fatalWaitingTime is an absurdly long time to wait for signals to be 36 // delivered but, using it, we (hopefully) eliminate test flakes on the 37 // build servers. See #46736 for discussion. 38 var fatalWaitingTime = 30 * time.Second 39 40 func init() { 41 if testenv.Builder() == "solaris-amd64-oraclerel" { 42 // The solaris-amd64-oraclerel builder has been observed to time out in 43 // TestNohup even with a 250ms settle time. 44 // 45 // Use a much longer settle time on that builder to try to suss out whether 46 // the test is flaky due to builder slowness (which may mean we need a 47 // longer GO_TEST_TIMEOUT_SCALE) or due to a dropped signal (which may 48 // instead need a test-skip and upstream bug filed against the Solaris 49 // kernel). 50 // 51 // This constant is chosen so as to make the test as generous as possible 52 // while still reliably completing within 3 minutes in non-short mode. 53 // 54 // See https://golang.org/issue/33174. 55 settleTime = 11 * time.Second 56 } else if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "ppc64") { 57 // Older linux kernels seem to have some hiccups delivering the signal 58 // in a timely manner on ppc64 and ppc64le. When running on a 59 // ppc64le/ubuntu 16.04/linux 4.4 host the time can vary quite 60 // substantially even on a idle system. 5 seconds is twice any value 61 // observed when running 10000 tests on such a system. 62 settleTime = 5 * time.Second 63 } else if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { 64 if scale, err := strconv.Atoi(s); err == nil { 65 settleTime *= time.Duration(scale) 66 } 67 } 68 } 69 70 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) { 71 t.Helper() 72 waitSig1(t, c, sig, false) 73 } 74 func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) { 75 t.Helper() 76 waitSig1(t, c, sig, true) 77 } 78 79 func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) { 80 t.Helper() 81 82 // Sleep multiple times to give the kernel more tries to 83 // deliver the signal. 84 start := time.Now() 85 timer := time.NewTimer(settleTime / 10) 86 defer timer.Stop() 87 // If the caller notified for all signals on c, filter out SIGURG, 88 // which is used for runtime preemption and can come at unpredictable times. 89 // General user code should filter out all unexpected signals instead of just 90 // SIGURG, but since os/signal is tightly coupled to the runtime it seems 91 // appropriate to be stricter here. 92 for time.Since(start) < fatalWaitingTime { 93 select { 94 case s := <-c: 95 if s == sig { 96 return 97 } 98 if !all || s != syscall.SIGURG { 99 t.Fatalf("signal was %v, want %v", s, sig) 100 } 101 case <-timer.C: 102 timer.Reset(settleTime / 10) 103 } 104 } 105 t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig) 106 } 107 108 // quiesce waits until we can be reasonably confident that all pending signals 109 // have been delivered by the OS. 110 func quiesce() { 111 // The kernel will deliver a signal as a thread returns 112 // from a syscall. If the only active thread is sleeping, 113 // and the system is busy, the kernel may not get around 114 // to waking up a thread to catch the signal. 115 // We try splitting up the sleep to give the kernel 116 // many chances to deliver the signal. 117 start := time.Now() 118 for time.Since(start) < settleTime { 119 time.Sleep(settleTime / 10) 120 } 121 } 122 123 // Test that basic signal handling works. 124 func TestSignal(t *testing.T) { 125 // Ask for SIGHUP 126 c := make(chan os.Signal, 1) 127 Notify(c, syscall.SIGHUP) 128 defer Stop(c) 129 130 // Send this process a SIGHUP 131 t.Logf("sighup...") 132 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 133 waitSig(t, c, syscall.SIGHUP) 134 135 // Ask for everything we can get. The buffer size has to be 136 // more than 1, since the runtime might send SIGURG signals. 137 // Using 10 is arbitrary. 138 c1 := make(chan os.Signal, 10) 139 Notify(c1) 140 141 // Send this process a SIGWINCH 142 t.Logf("sigwinch...") 143 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 144 waitSigAll(t, c1, syscall.SIGWINCH) 145 146 // Send two more SIGHUPs, to make sure that 147 // they get delivered on c1 and that not reading 148 // from c does not block everything. 149 t.Logf("sighup...") 150 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 151 waitSigAll(t, c1, syscall.SIGHUP) 152 t.Logf("sighup...") 153 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 154 waitSigAll(t, c1, syscall.SIGHUP) 155 156 // The first SIGHUP should be waiting for us on c. 157 waitSig(t, c, syscall.SIGHUP) 158 } 159 160 func TestStress(t *testing.T) { 161 dur := 3 * time.Second 162 if testing.Short() { 163 dur = 100 * time.Millisecond 164 } 165 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 166 167 sig := make(chan os.Signal, 1) 168 Notify(sig, syscall.SIGUSR1) 169 170 go func() { 171 stop := time.After(dur) 172 for { 173 select { 174 case <-stop: 175 // Allow enough time for all signals to be delivered before we stop 176 // listening for them. 177 quiesce() 178 Stop(sig) 179 // According to its documentation, “[w]hen Stop returns, it in 180 // guaranteed that c will receive no more signals.” So we can safely 181 // close sig here: if there is a send-after-close race here, that is a 182 // bug in Stop and we would like to detect it. 183 close(sig) 184 return 185 186 default: 187 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 188 runtime.Gosched() 189 } 190 } 191 }() 192 193 for range sig { 194 // Receive signals until the sender closes sig. 195 } 196 } 197 198 func testCancel(t *testing.T, ignore bool) { 199 // Ask to be notified on c1 when a SIGWINCH is received. 200 c1 := make(chan os.Signal, 1) 201 Notify(c1, syscall.SIGWINCH) 202 defer Stop(c1) 203 204 // Ask to be notified on c2 when a SIGHUP is received. 205 c2 := make(chan os.Signal, 1) 206 Notify(c2, syscall.SIGHUP) 207 defer Stop(c2) 208 209 // Send this process a SIGWINCH and wait for notification on c1. 210 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 211 waitSig(t, c1, syscall.SIGWINCH) 212 213 // Send this process a SIGHUP and wait for notification on c2. 214 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 215 waitSig(t, c2, syscall.SIGHUP) 216 217 // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP. 218 // Either way, this should undo both calls to Notify above. 219 if ignore { 220 Ignore(syscall.SIGWINCH, syscall.SIGHUP) 221 // Don't bother deferring a call to Reset: it is documented to undo Notify, 222 // but its documentation says nothing about Ignore, and (as of the time of 223 // writing) it empirically does not undo an Ignore. 224 } else { 225 Reset(syscall.SIGWINCH, syscall.SIGHUP) 226 } 227 228 // Send this process a SIGWINCH. It should be ignored. 229 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) 230 231 // If ignoring, Send this process a SIGHUP. It should be ignored. 232 if ignore { 233 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 234 } 235 236 quiesce() 237 238 select { 239 case s := <-c1: 240 t.Errorf("unexpected signal %v", s) 241 default: 242 // nothing to read - good 243 } 244 245 select { 246 case s := <-c2: 247 t.Errorf("unexpected signal %v", s) 248 default: 249 // nothing to read - good 250 } 251 252 // One or both of the signals may have been blocked for this process 253 // by the calling process. 254 // Discard any queued signals now to avoid interfering with other tests. 255 Notify(c1, syscall.SIGWINCH) 256 Notify(c2, syscall.SIGHUP) 257 quiesce() 258 } 259 260 // Test that Reset cancels registration for listed signals on all channels. 261 func TestReset(t *testing.T) { 262 testCancel(t, false) 263 } 264 265 // Test that Ignore cancels registration for listed signals on all channels. 266 func TestIgnore(t *testing.T) { 267 testCancel(t, true) 268 } 269 270 // Test that Ignored correctly detects changes to the ignored status of a signal. 271 func TestIgnored(t *testing.T) { 272 // Ask to be notified on SIGWINCH. 273 c := make(chan os.Signal, 1) 274 Notify(c, syscall.SIGWINCH) 275 276 // If we're being notified, then the signal should not be ignored. 277 if Ignored(syscall.SIGWINCH) { 278 t.Errorf("expected SIGWINCH to not be ignored.") 279 } 280 Stop(c) 281 Ignore(syscall.SIGWINCH) 282 283 // We're no longer paying attention to this signal. 284 if !Ignored(syscall.SIGWINCH) { 285 t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.") 286 } 287 288 Reset() 289 } 290 291 var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.") 292 293 // Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup. 294 func TestDetectNohup(t *testing.T) { 295 if *checkSighupIgnored { 296 if !Ignored(syscall.SIGHUP) { 297 t.Fatal("SIGHUP is not ignored.") 298 } else { 299 t.Log("SIGHUP is ignored.") 300 } 301 } else { 302 defer Reset() 303 // Ugly: ask for SIGHUP so that child will not have no-hup set 304 // even if test is running under nohup environment. 305 // We have no intention of reading from c. 306 c := make(chan os.Signal, 1) 307 Notify(c, syscall.SIGHUP) 308 if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil { 309 t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out) 310 } 311 Stop(c) 312 // Again, this time with nohup, assuming we can find it. 313 _, err := os.Stat("/usr/bin/nohup") 314 if err != nil { 315 t.Skip("cannot find nohup; skipping second half of test") 316 } 317 Ignore(syscall.SIGHUP) 318 os.Remove("nohup.out") 319 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput() 320 321 data, _ := os.ReadFile("nohup.out") 322 os.Remove("nohup.out") 323 if err != nil { 324 t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data) 325 } 326 } 327 } 328 329 var ( 330 sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") 331 dieFromSighup = flag.Bool("die_from_sighup", false, "wait to die from uncaught SIGHUP") 332 ) 333 334 // Test that Stop cancels the channel's registrations. 335 func TestStop(t *testing.T) { 336 sigs := []syscall.Signal{ 337 syscall.SIGWINCH, 338 syscall.SIGHUP, 339 syscall.SIGUSR1, 340 } 341 342 for _, sig := range sigs { 343 sig := sig 344 t.Run(fmt.Sprint(sig), func(t *testing.T) { 345 // When calling Notify with a specific signal, 346 // independent signals should not interfere with each other, 347 // and we end up needing to wait for signals to quiesce a lot. 348 // Test the three different signals concurrently. 349 t.Parallel() 350 351 // If the signal is not ignored, send the signal before registering a 352 // channel to verify the behavior of the default Go handler. 353 // If it's SIGWINCH or SIGUSR1 we should not see it. 354 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 355 mayHaveBlockedSignal := false 356 if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) { 357 syscall.Kill(syscall.Getpid(), sig) 358 quiesce() 359 360 // We don't know whether sig is blocked for this process; see 361 // https://golang.org/issue/38165. Assume that it could be. 362 mayHaveBlockedSignal = true 363 } 364 365 // Ask for signal 366 c := make(chan os.Signal, 1) 367 Notify(c, sig) 368 369 // Send this process the signal again. 370 syscall.Kill(syscall.Getpid(), sig) 371 waitSig(t, c, sig) 372 373 if mayHaveBlockedSignal { 374 // We may have received a queued initial signal in addition to the one 375 // that we sent after Notify. If so, waitSig may have observed that 376 // initial signal instead of the second one, and we may need to wait for 377 // the second signal to clear. Do that now. 378 quiesce() 379 select { 380 case <-c: 381 default: 382 } 383 } 384 385 // Stop watching for the signal and send it again. 386 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 387 Stop(c) 388 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { 389 syscall.Kill(syscall.Getpid(), sig) 390 quiesce() 391 392 select { 393 case s := <-c: 394 t.Errorf("unexpected signal %v", s) 395 default: 396 // nothing to read - good 397 } 398 399 // If we're going to receive a signal, it has almost certainly been 400 // received by now. However, it may have been blocked for this process — 401 // we don't know. Explicitly unblock it and wait for it to clear now. 402 Notify(c, sig) 403 quiesce() 404 Stop(c) 405 } 406 }) 407 } 408 } 409 410 // Test that when run under nohup, an uncaught SIGHUP does not kill the program. 411 func TestNohup(t *testing.T) { 412 // Ugly: ask for SIGHUP so that child will not have no-hup set 413 // even if test is running under nohup environment. 414 // We have no intention of reading from c. 415 c := make(chan os.Signal, 1) 416 Notify(c, syscall.SIGHUP) 417 418 // When run without nohup, the test should crash on an uncaught SIGHUP. 419 // When run under nohup, the test should ignore uncaught SIGHUPs, 420 // because the runtime is not supposed to be listening for them. 421 // Either way, TestStop should still be able to catch them when it wants them 422 // and then when it stops wanting them, the original behavior should resume. 423 // 424 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. 425 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. 426 // 427 // Both should fail without nohup and succeed with nohup. 428 429 var subTimeout time.Duration 430 431 var wg sync.WaitGroup 432 wg.Add(2) 433 if deadline, ok := t.Deadline(); ok { 434 subTimeout = time.Until(deadline) 435 subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output. 436 } 437 for i := 1; i <= 2; i++ { 438 i := i 439 go t.Run(fmt.Sprintf("uncaught-%d", i), func(t *testing.T) { 440 defer wg.Done() 441 442 args := []string{ 443 "-test.v", 444 "-test.run=TestStop", 445 "-send_uncaught_sighup=" + strconv.Itoa(i), 446 "-die_from_sighup", 447 } 448 if subTimeout != 0 { 449 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 450 } 451 out, err := exec.Command(os.Args[0], args...).CombinedOutput() 452 453 if err == nil { 454 t.Errorf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) 455 } else { 456 t.Logf("test with -send_uncaught_sighup=%d failed as expected.\nError: %v\nOutput:\n%s", i, err, out) 457 } 458 }) 459 } 460 wg.Wait() 461 462 Stop(c) 463 464 // Skip the nohup test below when running in tmux on darwin, since nohup 465 // doesn't work correctly there. See issue #5135. 466 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" { 467 t.Skip("Skipping nohup test due to running in tmux on darwin") 468 } 469 470 // Again, this time with nohup, assuming we can find it. 471 _, err := exec.LookPath("nohup") 472 if err != nil { 473 t.Skip("cannot find nohup; skipping second half of test") 474 } 475 476 wg.Add(2) 477 if deadline, ok := t.Deadline(); ok { 478 subTimeout = time.Until(deadline) 479 subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output. 480 } 481 for i := 1; i <= 2; i++ { 482 i := i 483 go t.Run(fmt.Sprintf("nohup-%d", i), func(t *testing.T) { 484 defer wg.Done() 485 486 // POSIX specifies that nohup writes to a file named nohup.out if standard 487 // output is a terminal. However, for an exec.Command, standard output is 488 // not a terminal — so we don't need to read or remove that file (and, 489 // indeed, cannot even create it if the current user is unable to write to 490 // GOROOT/src, such as when GOROOT is installed and owned by root). 491 492 args := []string{ 493 os.Args[0], 494 "-test.v", 495 "-test.run=TestStop", 496 "-send_uncaught_sighup=" + strconv.Itoa(i), 497 } 498 if subTimeout != 0 { 499 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 500 } 501 out, err := exec.Command("nohup", args...).CombinedOutput() 502 503 if err != nil { 504 t.Errorf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s", i, err, out) 505 } else { 506 t.Logf("ran test with -send_uncaught_sighup=%d under nohup.\nOutput:\n%s", i, out) 507 } 508 }) 509 } 510 wg.Wait() 511 } 512 513 // Test that SIGCONT works (issue 8953). 514 func TestSIGCONT(t *testing.T) { 515 c := make(chan os.Signal, 1) 516 Notify(c, syscall.SIGCONT) 517 defer Stop(c) 518 syscall.Kill(syscall.Getpid(), syscall.SIGCONT) 519 waitSig(t, c, syscall.SIGCONT) 520 } 521 522 // Test race between stopping and receiving a signal (issue 14571). 523 func TestAtomicStop(t *testing.T) { 524 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" { 525 atomicStopTestProgram(t) 526 t.Fatal("atomicStopTestProgram returned") 527 } 528 529 testenv.MustHaveExec(t) 530 531 // Call Notify for SIGINT before starting the child process. 532 // That ensures that SIGINT is not ignored for the child. 533 // This is necessary because if SIGINT is ignored when a 534 // Go program starts, then it remains ignored, and closing 535 // the last notification channel for SIGINT will switch it 536 // back to being ignored. In that case the assumption of 537 // atomicStopTestProgram, that it will either die from SIGINT 538 // or have it be reported, breaks down, as there is a third 539 // option: SIGINT might be ignored. 540 cs := make(chan os.Signal, 1) 541 Notify(cs, syscall.SIGINT) 542 defer Stop(cs) 543 544 const execs = 10 545 for i := 0; i < execs; i++ { 546 timeout := "0" 547 if deadline, ok := t.Deadline(); ok { 548 timeout = time.Until(deadline).String() 549 } 550 cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout) 551 cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1") 552 out, err := cmd.CombinedOutput() 553 if err == nil { 554 if len(out) > 0 { 555 t.Logf("iteration %d: output %s", i, out) 556 } 557 } else { 558 t.Logf("iteration %d: exit status %q: output: %s", i, err, out) 559 } 560 561 lost := bytes.Contains(out, []byte("lost signal")) 562 if lost { 563 t.Errorf("iteration %d: lost signal", i) 564 } 565 566 // The program should either die due to SIGINT, 567 // or exit with success without printing "lost signal". 568 if err == nil { 569 if len(out) > 0 && !lost { 570 t.Errorf("iteration %d: unexpected output", i) 571 } 572 } else { 573 if ee, ok := err.(*exec.ExitError); !ok { 574 t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err) 575 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 576 t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys()) 577 } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT { 578 t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee) 579 } 580 } 581 } 582 } 583 584 // atomicStopTestProgram is run in a subprocess by TestAtomicStop. 585 // It tries to trigger a signal delivery race. This function should 586 // either catch a signal or die from it. 587 func atomicStopTestProgram(t *testing.T) { 588 // This test won't work if SIGINT is ignored here. 589 if Ignored(syscall.SIGINT) { 590 fmt.Println("SIGINT is ignored") 591 os.Exit(1) 592 } 593 594 const tries = 10 595 596 timeout := 2 * time.Second 597 if deadline, ok := t.Deadline(); ok { 598 // Give each try an equal slice of the deadline, with one slice to spare for 599 // cleanup. 600 timeout = time.Until(deadline) / (tries + 1) 601 } 602 603 pid := syscall.Getpid() 604 printed := false 605 for i := 0; i < tries; i++ { 606 cs := make(chan os.Signal, 1) 607 Notify(cs, syscall.SIGINT) 608 609 var wg sync.WaitGroup 610 wg.Add(1) 611 go func() { 612 defer wg.Done() 613 Stop(cs) 614 }() 615 616 syscall.Kill(pid, syscall.SIGINT) 617 618 // At this point we should either die from SIGINT or 619 // get a notification on cs. If neither happens, we 620 // dropped the signal. It is given 2 seconds to 621 // deliver, as needed for gccgo on some loaded test systems. 622 623 select { 624 case <-cs: 625 case <-time.After(timeout): 626 if !printed { 627 fmt.Print("lost signal on tries:") 628 printed = true 629 } 630 fmt.Printf(" %d", i) 631 } 632 633 wg.Wait() 634 } 635 if printed { 636 fmt.Print("\n") 637 } 638 639 os.Exit(0) 640 } 641 642 func TestTime(t *testing.T) { 643 // Test that signal works fine when we are in a call to get time, 644 // which on some platforms is using VDSO. See issue #34391. 645 dur := 3 * time.Second 646 if testing.Short() { 647 dur = 100 * time.Millisecond 648 } 649 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 650 651 sig := make(chan os.Signal, 1) 652 Notify(sig, syscall.SIGUSR1) 653 654 stop := make(chan struct{}) 655 go func() { 656 for { 657 select { 658 case <-stop: 659 // Allow enough time for all signals to be delivered before we stop 660 // listening for them. 661 quiesce() 662 Stop(sig) 663 // According to its documentation, “[w]hen Stop returns, it in 664 // guaranteed that c will receive no more signals.” So we can safely 665 // close sig here: if there is a send-after-close race, that is a bug in 666 // Stop and we would like to detect it. 667 close(sig) 668 return 669 670 default: 671 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 672 runtime.Gosched() 673 } 674 } 675 }() 676 677 done := make(chan struct{}) 678 go func() { 679 for range sig { 680 // Receive signals until the sender closes sig. 681 } 682 close(done) 683 }() 684 685 t0 := time.Now() 686 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() { 687 } // hammering on getting time 688 689 close(stop) 690 <-done 691 } 692 693 var ( 694 checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.") 695 ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received") 696 ) 697 698 func TestNotifyContextNotifications(t *testing.T) { 699 if *checkNotifyContext { 700 ctx, _ := NotifyContext(context.Background(), syscall.SIGINT) 701 // We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal. 702 // Being able to wait for a number of received system signals allows us to do so. 703 var wg sync.WaitGroup 704 n := *ctxNotifyTimes 705 wg.Add(n) 706 for i := 0; i < n; i++ { 707 go func() { 708 syscall.Kill(syscall.Getpid(), syscall.SIGINT) 709 wg.Done() 710 }() 711 } 712 wg.Wait() 713 <-ctx.Done() 714 fmt.Print("received SIGINT") 715 // Sleep to give time to simultaneous signals to reach the process. 716 // These signals must be ignored given stop() is not called on this code. 717 // We want to guarantee a SIGINT doesn't cause a premature termination of the program. 718 time.Sleep(settleTime) 719 return 720 } 721 722 t.Parallel() 723 testCases := []struct { 724 name string 725 n int // number of times a SIGINT should be notified. 726 }{ 727 {"once", 1}, 728 {"multiple", 10}, 729 } 730 for _, tc := range testCases { 731 t.Run(tc.name, func(t *testing.T) { 732 var subTimeout time.Duration 733 if deadline, ok := t.Deadline(); ok { 734 subTimeout := time.Until(deadline) 735 subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess. 736 } 737 738 args := []string{ 739 "-test.v", 740 "-test.run=TestNotifyContextNotifications$", 741 "-check_notify_ctx", 742 fmt.Sprintf("-ctx_notify_times=%d", tc.n), 743 } 744 if subTimeout != 0 { 745 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 746 } 747 out, err := exec.Command(os.Args[0], args...).CombinedOutput() 748 if err != nil { 749 t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out) 750 } 751 if want := []byte("received SIGINT"); !bytes.Contains(out, want) { 752 t.Errorf("got %q, wanted %q", out, want) 753 } 754 }) 755 } 756 } 757 758 func TestNotifyContextStop(t *testing.T) { 759 Ignore(syscall.SIGHUP) 760 if !Ignored(syscall.SIGHUP) { 761 t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.") 762 } 763 764 parent, cancelParent := context.WithCancel(context.Background()) 765 defer cancelParent() 766 c, stop := NotifyContext(parent, syscall.SIGHUP) 767 defer stop() 768 769 // If we're being notified, then the signal should not be ignored. 770 if Ignored(syscall.SIGHUP) { 771 t.Errorf("expected SIGHUP to not be ignored.") 772 } 773 774 if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got { 775 t.Errorf("c.String() = %q, wanted %q", got, want) 776 } 777 778 stop() 779 select { 780 case <-c.Done(): 781 if got := c.Err(); got != context.Canceled { 782 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 783 } 784 case <-time.After(time.Second): 785 t.Errorf("timed out waiting for context to be done after calling stop") 786 } 787 } 788 789 func TestNotifyContextCancelParent(t *testing.T) { 790 parent, cancelParent := context.WithCancel(context.Background()) 791 defer cancelParent() 792 c, stop := NotifyContext(parent, syscall.SIGINT) 793 defer stop() 794 795 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { 796 t.Errorf("c.String() = %q, want %q", got, want) 797 } 798 799 cancelParent() 800 select { 801 case <-c.Done(): 802 if got := c.Err(); got != context.Canceled { 803 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 804 } 805 case <-time.After(time.Second): 806 t.Errorf("timed out waiting for parent context to be canceled") 807 } 808 } 809 810 func TestNotifyContextPrematureCancelParent(t *testing.T) { 811 parent, cancelParent := context.WithCancel(context.Background()) 812 defer cancelParent() 813 814 cancelParent() // Prematurely cancel context before calling NotifyContext. 815 c, stop := NotifyContext(parent, syscall.SIGINT) 816 defer stop() 817 818 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { 819 t.Errorf("c.String() = %q, want %q", got, want) 820 } 821 822 select { 823 case <-c.Done(): 824 if got := c.Err(); got != context.Canceled { 825 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 826 } 827 case <-time.After(time.Second): 828 t.Errorf("timed out waiting for parent context to be canceled") 829 } 830 } 831 832 func TestNotifyContextSimultaneousStop(t *testing.T) { 833 c, stop := NotifyContext(context.Background(), syscall.SIGINT) 834 defer stop() 835 836 if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got { 837 t.Errorf("c.String() = %q, want %q", got, want) 838 } 839 840 var wg sync.WaitGroup 841 n := 10 842 wg.Add(n) 843 for i := 0; i < n; i++ { 844 go func() { 845 stop() 846 wg.Done() 847 }() 848 } 849 wg.Wait() 850 select { 851 case <-c.Done(): 852 if got := c.Err(); got != context.Canceled { 853 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 854 } 855 case <-time.After(time.Second): 856 t.Errorf("expected context to be canceled") 857 } 858 } 859 860 func TestNotifyContextStringer(t *testing.T) { 861 parent, cancelParent := context.WithCancel(context.Background()) 862 defer cancelParent() 863 c, stop := NotifyContext(parent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) 864 defer stop() 865 866 want := `signal.NotifyContext(context.Background.WithCancel, [hangup interrupt terminated])` 867 if got := fmt.Sprint(c); got != want { 868 t.Errorf("c.String() = %q, want %q", got, want) 869 } 870 } 871 872 // #44193 test signal handling while stopping and starting the world. 873 func TestSignalTrace(t *testing.T) { 874 done := make(chan struct{}) 875 quit := make(chan struct{}) 876 c := make(chan os.Signal, 1) 877 Notify(c, syscall.SIGHUP) 878 879 // Source and sink for signals busy loop unsynchronized with 880 // trace starts and stops. We are ultimately validating that 881 // signals and runtime.(stop|start)TheWorldGC are compatible. 882 go func() { 883 defer close(done) 884 defer Stop(c) 885 pid := syscall.Getpid() 886 for { 887 select { 888 case <-quit: 889 return 890 default: 891 syscall.Kill(pid, syscall.SIGHUP) 892 } 893 waitSig(t, c, syscall.SIGHUP) 894 } 895 }() 896 897 for i := 0; i < 100; i++ { 898 buf := new(bytes.Buffer) 899 if err := trace.Start(buf); err != nil { 900 t.Fatalf("[%d] failed to start tracing: %v", i, err) 901 } 902 time.After(1 * time.Microsecond) 903 trace.Stop() 904 size := buf.Len() 905 if size == 0 { 906 t.Fatalf("[%d] trace is empty", i) 907 } 908 } 909 close(quit) 910 <-done 911 }