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