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