github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 an 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 := testenv.Command(t, 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 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 := testenv.Command(t, "/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 // nohup doesn't work on new LUCI darwin builders due to the 325 // type of launchd service the test run under. See 326 // https://go.dev/issue/63875. 327 if runtime.GOOS == "darwin" && strings.Contains(string(out), "nohup: can't detach from console: Inappropriate ioctl for device") { 328 t.Skip("Skipping nohup test due to darwin builder limitation. See https://go.dev/issue/63875.") 329 } 330 331 t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data) 332 } 333 } 334 } 335 336 var ( 337 sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop") 338 dieFromSighup = flag.Bool("die_from_sighup", false, "wait to die from uncaught SIGHUP") 339 ) 340 341 // Test that Stop cancels the channel's registrations. 342 func TestStop(t *testing.T) { 343 sigs := []syscall.Signal{ 344 syscall.SIGWINCH, 345 syscall.SIGHUP, 346 syscall.SIGUSR1, 347 } 348 349 for _, sig := range sigs { 350 sig := sig 351 t.Run(fmt.Sprint(sig), func(t *testing.T) { 352 // When calling Notify with a specific signal, 353 // independent signals should not interfere with each other, 354 // and we end up needing to wait for signals to quiesce a lot. 355 // Test the three different signals concurrently. 356 t.Parallel() 357 358 // If the signal is not ignored, send the signal before registering a 359 // channel to verify the behavior of the default Go handler. 360 // If it's SIGWINCH or SIGUSR1 we should not see it. 361 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 362 mayHaveBlockedSignal := false 363 if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) { 364 syscall.Kill(syscall.Getpid(), sig) 365 quiesce() 366 367 // We don't know whether sig is blocked for this process; see 368 // https://golang.org/issue/38165. Assume that it could be. 369 mayHaveBlockedSignal = true 370 } 371 372 // Ask for signal 373 c := make(chan os.Signal, 1) 374 Notify(c, sig) 375 376 // Send this process the signal again. 377 syscall.Kill(syscall.Getpid(), sig) 378 waitSig(t, c, sig) 379 380 if mayHaveBlockedSignal { 381 // We may have received a queued initial signal in addition to the one 382 // that we sent after Notify. If so, waitSig may have observed that 383 // initial signal instead of the second one, and we may need to wait for 384 // the second signal to clear. Do that now. 385 quiesce() 386 select { 387 case <-c: 388 default: 389 } 390 } 391 392 // Stop watching for the signal and send it again. 393 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do. 394 Stop(c) 395 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 { 396 syscall.Kill(syscall.Getpid(), sig) 397 quiesce() 398 399 select { 400 case s := <-c: 401 t.Errorf("unexpected signal %v", s) 402 default: 403 // nothing to read - good 404 } 405 406 // If we're going to receive a signal, it has almost certainly been 407 // received by now. However, it may have been blocked for this process — 408 // we don't know. Explicitly unblock it and wait for it to clear now. 409 Notify(c, sig) 410 quiesce() 411 Stop(c) 412 } 413 }) 414 } 415 } 416 417 // Test that when run under nohup, an uncaught SIGHUP does not kill the program. 418 func TestNohup(t *testing.T) { 419 // When run without nohup, the test should crash on an uncaught SIGHUP. 420 // When run under nohup, the test should ignore uncaught SIGHUPs, 421 // because the runtime is not supposed to be listening for them. 422 // Either way, TestStop should still be able to catch them when it wants them 423 // and then when it stops wanting them, the original behavior should resume. 424 // 425 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs. 426 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs. 427 // 428 // Both should fail without nohup and succeed with nohup. 429 430 t.Run("uncaught", func(t *testing.T) { 431 // Ugly: ask for SIGHUP so that child will not have no-hup set 432 // even if test is running under nohup environment. 433 // We have no intention of reading from c. 434 c := make(chan os.Signal, 1) 435 Notify(c, syscall.SIGHUP) 436 t.Cleanup(func() { Stop(c) }) 437 438 var subTimeout time.Duration 439 if deadline, ok := t.Deadline(); ok { 440 subTimeout = time.Until(deadline) 441 subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output. 442 } 443 for i := 1; i <= 2; i++ { 444 i := i 445 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 446 t.Parallel() 447 448 args := []string{ 449 "-test.v", 450 "-test.run=^TestStop$", 451 "-send_uncaught_sighup=" + strconv.Itoa(i), 452 "-die_from_sighup", 453 } 454 if subTimeout != 0 { 455 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 456 } 457 out, err := testenv.Command(t, os.Args[0], args...).CombinedOutput() 458 459 if err == nil { 460 t.Errorf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out) 461 } else { 462 t.Logf("test with -send_uncaught_sighup=%d failed as expected.\nError: %v\nOutput:\n%s", i, err, out) 463 } 464 }) 465 } 466 }) 467 468 t.Run("nohup", func(t *testing.T) { 469 // Skip the nohup test below when running in tmux on darwin, since nohup 470 // doesn't work correctly there. See issue #5135. 471 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" { 472 t.Skip("Skipping nohup test due to running in tmux on darwin") 473 } 474 475 // Again, this time with nohup, assuming we can find it. 476 _, err := exec.LookPath("nohup") 477 if err != nil { 478 t.Skip("cannot find nohup; skipping second half of test") 479 } 480 481 var subTimeout time.Duration 482 if deadline, ok := t.Deadline(); ok { 483 subTimeout = time.Until(deadline) 484 subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output. 485 } 486 for i := 1; i <= 2; i++ { 487 i := i 488 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 489 t.Parallel() 490 491 // POSIX specifies that nohup writes to a file named nohup.out if standard 492 // output is a terminal. However, for an exec.Cmd, standard output is 493 // not a terminal — so we don't need to read or remove that file (and, 494 // indeed, cannot even create it if the current user is unable to write to 495 // GOROOT/src, such as when GOROOT is installed and owned by root). 496 497 args := []string{ 498 os.Args[0], 499 "-test.v", 500 "-test.run=^TestStop$", 501 "-send_uncaught_sighup=" + strconv.Itoa(i), 502 } 503 if subTimeout != 0 { 504 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 505 } 506 out, err := testenv.Command(t, "nohup", args...).CombinedOutput() 507 508 if err != nil { 509 // nohup doesn't work on new LUCI darwin builders due to the 510 // type of launchd service the test run under. See 511 // https://go.dev/issue/63875. 512 if runtime.GOOS == "darwin" && strings.Contains(string(out), "nohup: can't detach from console: Inappropriate ioctl for device") { 513 // TODO(go.dev/issue/63799): A false-positive in vet reports a 514 // t.Skip here as invalid. Switch back to t.Skip once fixed. 515 t.Logf("Skipping nohup test due to darwin builder limitation. See https://go.dev/issue/63875.") 516 return 517 } 518 519 t.Errorf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s", i, err, out) 520 } else { 521 t.Logf("ran test with -send_uncaught_sighup=%d under nohup.\nOutput:\n%s", i, out) 522 } 523 }) 524 } 525 }) 526 } 527 528 // Test that SIGCONT works (issue 8953). 529 func TestSIGCONT(t *testing.T) { 530 c := make(chan os.Signal, 1) 531 Notify(c, syscall.SIGCONT) 532 defer Stop(c) 533 syscall.Kill(syscall.Getpid(), syscall.SIGCONT) 534 waitSig(t, c, syscall.SIGCONT) 535 } 536 537 // Test race between stopping and receiving a signal (issue 14571). 538 func TestAtomicStop(t *testing.T) { 539 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" { 540 atomicStopTestProgram(t) 541 t.Fatal("atomicStopTestProgram returned") 542 } 543 544 testenv.MustHaveExec(t) 545 546 // Call Notify for SIGINT before starting the child process. 547 // That ensures that SIGINT is not ignored for the child. 548 // This is necessary because if SIGINT is ignored when a 549 // Go program starts, then it remains ignored, and closing 550 // the last notification channel for SIGINT will switch it 551 // back to being ignored. In that case the assumption of 552 // atomicStopTestProgram, that it will either die from SIGINT 553 // or have it be reported, breaks down, as there is a third 554 // option: SIGINT might be ignored. 555 cs := make(chan os.Signal, 1) 556 Notify(cs, syscall.SIGINT) 557 defer Stop(cs) 558 559 const execs = 10 560 for i := 0; i < execs; i++ { 561 timeout := "0" 562 if deadline, ok := t.Deadline(); ok { 563 timeout = time.Until(deadline).String() 564 } 565 cmd := testenv.Command(t, os.Args[0], "-test.run=^TestAtomicStop$", "-test.timeout="+timeout) 566 cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1") 567 out, err := cmd.CombinedOutput() 568 if err == nil { 569 if len(out) > 0 { 570 t.Logf("iteration %d: output %s", i, out) 571 } 572 } else { 573 t.Logf("iteration %d: exit status %q: output: %s", i, err, out) 574 } 575 576 lost := bytes.Contains(out, []byte("lost signal")) 577 if lost { 578 t.Errorf("iteration %d: lost signal", i) 579 } 580 581 // The program should either die due to SIGINT, 582 // or exit with success without printing "lost signal". 583 if err == nil { 584 if len(out) > 0 && !lost { 585 t.Errorf("iteration %d: unexpected output", i) 586 } 587 } else { 588 if ee, ok := err.(*exec.ExitError); !ok { 589 t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err) 590 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 591 t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys()) 592 } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT { 593 t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee) 594 } 595 } 596 } 597 } 598 599 // atomicStopTestProgram is run in a subprocess by TestAtomicStop. 600 // It tries to trigger a signal delivery race. This function should 601 // either catch a signal or die from it. 602 func atomicStopTestProgram(t *testing.T) { 603 // This test won't work if SIGINT is ignored here. 604 if Ignored(syscall.SIGINT) { 605 fmt.Println("SIGINT is ignored") 606 os.Exit(1) 607 } 608 609 const tries = 10 610 611 timeout := 2 * time.Second 612 if deadline, ok := t.Deadline(); ok { 613 // Give each try an equal slice of the deadline, with one slice to spare for 614 // cleanup. 615 timeout = time.Until(deadline) / (tries + 1) 616 } 617 618 pid := syscall.Getpid() 619 printed := false 620 for i := 0; i < tries; i++ { 621 cs := make(chan os.Signal, 1) 622 Notify(cs, syscall.SIGINT) 623 624 var wg sync.WaitGroup 625 wg.Add(1) 626 go func() { 627 defer wg.Done() 628 Stop(cs) 629 }() 630 631 syscall.Kill(pid, syscall.SIGINT) 632 633 // At this point we should either die from SIGINT or 634 // get a notification on cs. If neither happens, we 635 // dropped the signal. It is given 2 seconds to 636 // deliver, as needed for gccgo on some loaded test systems. 637 638 select { 639 case <-cs: 640 case <-time.After(timeout): 641 if !printed { 642 fmt.Print("lost signal on tries:") 643 printed = true 644 } 645 fmt.Printf(" %d", i) 646 } 647 648 wg.Wait() 649 } 650 if printed { 651 fmt.Print("\n") 652 } 653 654 os.Exit(0) 655 } 656 657 func TestTime(t *testing.T) { 658 // Test that signal works fine when we are in a call to get time, 659 // which on some platforms is using VDSO. See issue #34391. 660 dur := 3 * time.Second 661 if testing.Short() { 662 dur = 100 * time.Millisecond 663 } 664 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 665 666 sig := make(chan os.Signal, 1) 667 Notify(sig, syscall.SIGUSR1) 668 669 stop := make(chan struct{}) 670 go func() { 671 for { 672 select { 673 case <-stop: 674 // Allow enough time for all signals to be delivered before we stop 675 // listening for them. 676 quiesce() 677 Stop(sig) 678 // According to its documentation, “[w]hen Stop returns, it in 679 // guaranteed that c will receive no more signals.” So we can safely 680 // close sig here: if there is a send-after-close race, that is a bug in 681 // Stop and we would like to detect it. 682 close(sig) 683 return 684 685 default: 686 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1) 687 runtime.Gosched() 688 } 689 } 690 }() 691 692 done := make(chan struct{}) 693 go func() { 694 for range sig { 695 // Receive signals until the sender closes sig. 696 } 697 close(done) 698 }() 699 700 t0 := time.Now() 701 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() { 702 } // hammering on getting time 703 704 close(stop) 705 <-done 706 } 707 708 var ( 709 checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.") 710 ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received") 711 ) 712 713 func TestNotifyContextNotifications(t *testing.T) { 714 if *checkNotifyContext { 715 ctx, _ := NotifyContext(context.Background(), syscall.SIGINT) 716 // We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal. 717 // Being able to wait for a number of received system signals allows us to do so. 718 var wg sync.WaitGroup 719 n := *ctxNotifyTimes 720 wg.Add(n) 721 for i := 0; i < n; i++ { 722 go func() { 723 syscall.Kill(syscall.Getpid(), syscall.SIGINT) 724 wg.Done() 725 }() 726 } 727 wg.Wait() 728 <-ctx.Done() 729 fmt.Println("received SIGINT") 730 // Sleep to give time to simultaneous signals to reach the process. 731 // These signals must be ignored given stop() is not called on this code. 732 // We want to guarantee a SIGINT doesn't cause a premature termination of the program. 733 time.Sleep(settleTime) 734 return 735 } 736 737 t.Parallel() 738 testCases := []struct { 739 name string 740 n int // number of times a SIGINT should be notified. 741 }{ 742 {"once", 1}, 743 {"multiple", 10}, 744 } 745 for _, tc := range testCases { 746 tc := tc 747 t.Run(tc.name, func(t *testing.T) { 748 t.Parallel() 749 750 var subTimeout time.Duration 751 if deadline, ok := t.Deadline(); ok { 752 timeout := time.Until(deadline) 753 if timeout < 2*settleTime { 754 t.Fatalf("starting test with less than %v remaining", 2*settleTime) 755 } 756 subTimeout = timeout - (timeout / 10) // Leave 10% headroom for cleaning up subprocess. 757 } 758 759 args := []string{ 760 "-test.v", 761 "-test.run=^TestNotifyContextNotifications$", 762 "-check_notify_ctx", 763 fmt.Sprintf("-ctx_notify_times=%d", tc.n), 764 } 765 if subTimeout != 0 { 766 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout)) 767 } 768 out, err := testenv.Command(t, os.Args[0], args...).CombinedOutput() 769 if err != nil { 770 t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out) 771 } 772 if want := []byte("received SIGINT\n"); !bytes.Contains(out, want) { 773 t.Errorf("got %q, wanted %q", out, want) 774 } 775 }) 776 } 777 } 778 779 func TestNotifyContextStop(t *testing.T) { 780 Ignore(syscall.SIGHUP) 781 if !Ignored(syscall.SIGHUP) { 782 t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.") 783 } 784 785 parent, cancelParent := context.WithCancel(context.Background()) 786 defer cancelParent() 787 c, stop := NotifyContext(parent, syscall.SIGHUP) 788 defer stop() 789 790 // If we're being notified, then the signal should not be ignored. 791 if Ignored(syscall.SIGHUP) { 792 t.Errorf("expected SIGHUP to not be ignored.") 793 } 794 795 if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got { 796 t.Errorf("c.String() = %q, wanted %q", got, want) 797 } 798 799 stop() 800 <-c.Done() 801 if got := c.Err(); got != context.Canceled { 802 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 803 } 804 } 805 806 func TestNotifyContextCancelParent(t *testing.T) { 807 parent, cancelParent := context.WithCancel(context.Background()) 808 defer cancelParent() 809 c, stop := NotifyContext(parent, syscall.SIGINT) 810 defer stop() 811 812 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { 813 t.Errorf("c.String() = %q, want %q", got, want) 814 } 815 816 cancelParent() 817 <-c.Done() 818 if got := c.Err(); got != context.Canceled { 819 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 820 } 821 } 822 823 func TestNotifyContextPrematureCancelParent(t *testing.T) { 824 parent, cancelParent := context.WithCancel(context.Background()) 825 defer cancelParent() 826 827 cancelParent() // Prematurely cancel context before calling NotifyContext. 828 c, stop := NotifyContext(parent, syscall.SIGINT) 829 defer stop() 830 831 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got { 832 t.Errorf("c.String() = %q, want %q", got, want) 833 } 834 835 <-c.Done() 836 if got := c.Err(); got != context.Canceled { 837 t.Errorf("c.Err() = %q, want %q", got, context.Canceled) 838 } 839 } 840 841 func TestNotifyContextSimultaneousStop(t *testing.T) { 842 c, stop := NotifyContext(context.Background(), syscall.SIGINT) 843 defer stop() 844 845 if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got { 846 t.Errorf("c.String() = %q, want %q", got, want) 847 } 848 849 var wg sync.WaitGroup 850 n := 10 851 wg.Add(n) 852 for i := 0; i < n; i++ { 853 go func() { 854 stop() 855 wg.Done() 856 }() 857 } 858 wg.Wait() 859 <-c.Done() 860 if got := c.Err(); got != context.Canceled { 861 t.Errorf("c.Err() = %q, want %q", got, context.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 trace.Stop() 908 size := buf.Len() 909 if size == 0 { 910 t.Fatalf("[%d] trace is empty", i) 911 } 912 } 913 close(quit) 914 <-done 915 }