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