testsuite, coroutines: Add tests for non-supension ramp returns.
[official-gcc.git] / libgo / go / os / signal / signal_test.go
blob7c8162ec7d7990a484eb6fff1348e0f8669be097
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.
5 //go:build aix || darwin || dragonfly || freebsd || hurd || linux || netbsd || openbsd || solaris
7 package signal
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"
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.
31 // The current value is set based on flakes observed in the Go builders.
32 var settleTime = 100 * time.Millisecond
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
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.
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).
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.
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)
69 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
70 t.Helper()
71 waitSig1(t, c, sig, false)
73 func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
74 t.Helper()
75 waitSig1(t, c, sig, true)
78 func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
79 t.Helper()
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
97 if !all || s != syscall.SIGURG {
98 t.Fatalf("signal was %v, want %v", s, sig)
100 case <-timer.C:
101 timer.Reset(settleTime / 10)
104 t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig)
107 // quiesce waits until we can be reasonably confident that all pending signals
108 // have been delivered by the OS.
109 func 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)
122 // Test that basic signal handling works.
123 func TestSignal(t *testing.T) {
124 // Ask for SIGHUP
125 c := make(chan os.Signal, 1)
126 Notify(c, syscall.SIGHUP)
127 defer Stop(c)
129 // Send this process a SIGHUP
130 t.Logf("sighup...")
131 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
132 waitSig(t, c, syscall.SIGHUP)
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)
143 // Send this process a SIGWINCH
144 t.Logf("sigwinch...")
145 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
146 waitSigAll(t, c1, syscall.SIGWINCH)
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)
158 // The first SIGHUP should be waiting for us on c.
159 waitSig(t, c, syscall.SIGHUP)
162 func TestStress(t *testing.T) {
163 dur := 3 * time.Second
164 if testing.Short() {
165 dur = 100 * time.Millisecond
167 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
169 sig := make(chan os.Signal, 1)
170 Notify(sig, syscall.SIGUSR1)
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
188 default:
189 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
190 runtime.Gosched()
195 for range sig {
196 // Receive signals until the sender closes sig.
200 func 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)
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)
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)
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)
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)
230 // Send this process a SIGWINCH. It should be ignored.
231 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
233 // If ignoring, Send this process a SIGHUP. It should be ignored.
234 if ignore {
235 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
238 quiesce()
240 select {
241 case s := <-c1:
242 t.Errorf("unexpected signal %v", s)
243 default:
244 // nothing to read - good
247 select {
248 case s := <-c2:
249 t.Errorf("unexpected signal %v", s)
250 default:
251 // nothing to read - good
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()
262 // Test that Reset cancels registration for listed signals on all channels.
263 func TestReset(t *testing.T) {
264 testCancel(t, false)
267 // Test that Ignore cancels registration for listed signals on all channels.
268 func TestIgnore(t *testing.T) {
269 testCancel(t, true)
272 // Test that Ignored correctly detects changes to the ignored status of a signal.
273 func TestIgnored(t *testing.T) {
274 // Ask to be notified on SIGWINCH.
275 c := make(chan os.Signal, 1)
276 Notify(c, syscall.SIGWINCH)
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.")
282 Stop(c)
283 Ignore(syscall.SIGWINCH)
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.")
290 Reset()
293 var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.")
295 // Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup.
296 func 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.")
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)
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")
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()
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)
331 var (
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")
336 // Test that Stop cancels the channel's registrations.
337 func TestStop(t *testing.T) {
338 sigs := []syscall.Signal{
339 syscall.SIGWINCH,
340 syscall.SIGHUP,
341 syscall.SIGUSR1,
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()
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()
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
367 // Ask for signal
368 c := make(chan os.Signal, 1)
369 Notify(c, sig)
371 // Send this process the signal again.
372 syscall.Kill(syscall.Getpid(), sig)
373 waitSig(t, c, sig)
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:
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()
394 select {
395 case s := <-c:
396 t.Errorf("unexpected signal %v", s)
397 default:
398 // nothing to read - good
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)
412 // Test that when run under nohup, an uncaught SIGHUP does not kill the program.
413 func 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)
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.
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.
429 // Both should fail without nohup and succeed with nohup.
431 var subTimeout time.Duration
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.
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()
444 args := []string{
445 "-test.v",
446 "-test.run=TestStop",
447 "-send_uncaught_sighup=" + strconv.Itoa(i),
448 "-die_from_sighup",
450 if subTimeout != 0 {
451 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
453 out, err := exec.Command(os.Args[0], args...).CombinedOutput()
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)
462 wg.Wait()
464 Stop(c)
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")
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")
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.
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()
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).
494 args := []string{
495 os.Args[0],
496 "-test.v",
497 "-test.run=TestStop",
498 "-send_uncaught_sighup=" + strconv.Itoa(i),
500 if subTimeout != 0 {
501 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
503 out, err := exec.Command("nohup", args...).CombinedOutput()
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)
512 wg.Wait()
515 // Test that SIGCONT works (issue 8953).
516 func 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)
524 // Test race between stopping and receiving a signal (issue 14571).
525 func TestAtomicStop(t *testing.T) {
526 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
527 atomicStopTestProgram(t)
528 t.Fatal("atomicStopTestProgram returned")
531 testenv.MustHaveExec(t)
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)
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()
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)
559 } else {
560 t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
563 lost := bytes.Contains(out, []byte("lost signal"))
564 if lost {
565 t.Errorf("iteration %d: lost signal", i)
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)
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)
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.
589 func 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)
596 const tries = 10
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)
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)
611 var wg sync.WaitGroup
612 wg.Add(1)
613 go func() {
614 defer wg.Done()
615 Stop(cs)
618 syscall.Kill(pid, syscall.SIGINT)
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.
625 select {
626 case <-cs:
627 case <-time.After(timeout):
628 if !printed {
629 fmt.Print("lost signal on tries:")
630 printed = true
632 fmt.Printf(" %d", i)
635 wg.Wait()
637 if printed {
638 fmt.Print("\n")
641 os.Exit(0)
644 func 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
651 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
653 sig := make(chan os.Signal, 1)
654 Notify(sig, syscall.SIGUSR1)
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
672 default:
673 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
674 runtime.Gosched()
679 done := make(chan struct{})
680 go func() {
681 for range sig {
682 // Receive signals until the sender closes sig.
684 close(done)
687 t0 := time.Now()
688 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
689 } // hammering on getting time
691 close(stop)
692 <-done
695 var (
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")
700 func 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()
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
724 t.Parallel()
725 testCases := []struct {
726 name string
727 n int // number of times a SIGINT should be notified.
729 {"once", 1},
730 {"multiple", 10},
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.
740 args := []string{
741 "-test.v",
742 "-test.run=TestNotifyContextNotifications$",
743 "-check_notify_ctx",
744 fmt.Sprintf("-ctx_notify_times=%d", tc.n),
746 if subTimeout != 0 {
747 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
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)
753 if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
754 t.Errorf("got %q, wanted %q", out, want)
760 func 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.")
766 parent, cancelParent := context.WithCancel(context.Background())
767 defer cancelParent()
768 c, stop := NotifyContext(parent, syscall.SIGHUP)
769 defer stop()
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.")
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)
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)
786 case <-time.After(time.Second):
787 t.Errorf("timed out waiting for context to be done after calling stop")
791 func TestNotifyContextCancelParent(t *testing.T) {
792 parent, cancelParent := context.WithCancel(context.Background())
793 defer cancelParent()
794 c, stop := NotifyContext(parent, syscall.SIGINT)
795 defer stop()
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)
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)
807 case <-time.After(time.Second):
808 t.Errorf("timed out waiting for parent context to be canceled")
812 func TestNotifyContextPrematureCancelParent(t *testing.T) {
813 parent, cancelParent := context.WithCancel(context.Background())
814 defer cancelParent()
816 cancelParent() // Prematurely cancel context before calling NotifyContext.
817 c, stop := NotifyContext(parent, syscall.SIGINT)
818 defer stop()
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)
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)
829 case <-time.After(time.Second):
830 t.Errorf("timed out waiting for parent context to be canceled")
834 func TestNotifyContextSimultaneousStop(t *testing.T) {
835 c, stop := NotifyContext(context.Background(), syscall.SIGINT)
836 defer stop()
838 if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
839 t.Errorf("c.String() = %q, want %q", got, want)
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()
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)
857 case <-time.After(time.Second):
858 t.Errorf("expected context to be canceled")
862 func 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()
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)
874 // #44193 test signal handling while stopping and starting the world.
875 func 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)
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)
895 waitSig(t, c, syscall.SIGHUP)
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)
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)
911 close(quit)
912 <-done