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