Source file
src/os/signal/signal_test.go
1
2
3
4
5
6
7
8 package signal
9
10 import (
11 "bytes"
12 "context"
13 "flag"
14 "fmt"
15 "internal/testenv"
16 "os"
17 "os/exec"
18 "runtime"
19 "runtime/trace"
20 "strconv"
21 "strings"
22 "sync"
23 "syscall"
24 "testing"
25 "time"
26 )
27
28
29
30
31
32
33 var settleTime = 100 * time.Millisecond
34
35
36
37
38 var fatalWaitingTime = 30 * time.Second
39
40 func init() {
41 if testenv.Builder() == "solaris-amd64-oraclerel" {
42
43
44
45
46
47
48
49
50
51
52
53
54
55 settleTime = 11 * time.Second
56 } else if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "ppc64") {
57
58
59
60
61
62 settleTime = 5 * time.Second
63 } else if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
64 if scale, err := strconv.Atoi(s); err == nil {
65 settleTime *= time.Duration(scale)
66 }
67 }
68 }
69
70 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
71 t.Helper()
72 waitSig1(t, c, sig, false)
73 }
74 func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
75 t.Helper()
76 waitSig1(t, c, sig, true)
77 }
78
79 func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
80 t.Helper()
81
82
83
84 start := time.Now()
85 timer := time.NewTimer(settleTime / 10)
86 defer timer.Stop()
87
88
89
90
91
92 for time.Since(start) < fatalWaitingTime {
93 select {
94 case s := <-c:
95 if s == sig {
96 return
97 }
98 if !all || s != syscall.SIGURG {
99 t.Fatalf("signal was %v, want %v", s, sig)
100 }
101 case <-timer.C:
102 timer.Reset(settleTime / 10)
103 }
104 }
105 t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig)
106 }
107
108
109
110 func quiesce() {
111
112
113
114
115
116
117 start := time.Now()
118 for time.Since(start) < settleTime {
119 time.Sleep(settleTime / 10)
120 }
121 }
122
123
124 func TestSignal(t *testing.T) {
125
126 c := make(chan os.Signal, 1)
127 Notify(c, syscall.SIGHUP)
128 defer Stop(c)
129
130
131 t.Logf("sighup...")
132 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
133 waitSig(t, c, syscall.SIGHUP)
134
135
136
137
138 c1 := make(chan os.Signal, 10)
139 Notify(c1)
140
141
142 t.Logf("sigwinch...")
143 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
144 waitSigAll(t, c1, syscall.SIGWINCH)
145
146
147
148
149 t.Logf("sighup...")
150 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
151 waitSigAll(t, c1, syscall.SIGHUP)
152 t.Logf("sighup...")
153 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
154 waitSigAll(t, c1, syscall.SIGHUP)
155
156
157 waitSig(t, c, syscall.SIGHUP)
158 }
159
160 func TestStress(t *testing.T) {
161 dur := 3 * time.Second
162 if testing.Short() {
163 dur = 100 * time.Millisecond
164 }
165 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
166
167 sig := make(chan os.Signal, 1)
168 Notify(sig, syscall.SIGUSR1)
169
170 go func() {
171 stop := time.After(dur)
172 for {
173 select {
174 case <-stop:
175
176
177 quiesce()
178 Stop(sig)
179
180
181
182
183 close(sig)
184 return
185
186 default:
187 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
188 runtime.Gosched()
189 }
190 }
191 }()
192
193 for range sig {
194
195 }
196 }
197
198 func testCancel(t *testing.T, ignore bool) {
199
200 c1 := make(chan os.Signal, 1)
201 Notify(c1, syscall.SIGWINCH)
202 defer Stop(c1)
203
204
205 c2 := make(chan os.Signal, 1)
206 Notify(c2, syscall.SIGHUP)
207 defer Stop(c2)
208
209
210 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
211 waitSig(t, c1, syscall.SIGWINCH)
212
213
214 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
215 waitSig(t, c2, syscall.SIGHUP)
216
217
218
219 if ignore {
220 Ignore(syscall.SIGWINCH, syscall.SIGHUP)
221
222
223
224 } else {
225 Reset(syscall.SIGWINCH, syscall.SIGHUP)
226 }
227
228
229 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
230
231
232 if ignore {
233 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
234 }
235
236 quiesce()
237
238 select {
239 case s := <-c1:
240 t.Errorf("unexpected signal %v", s)
241 default:
242
243 }
244
245 select {
246 case s := <-c2:
247 t.Errorf("unexpected signal %v", s)
248 default:
249
250 }
251
252
253
254
255 Notify(c1, syscall.SIGWINCH)
256 Notify(c2, syscall.SIGHUP)
257 quiesce()
258 }
259
260
261 func TestReset(t *testing.T) {
262 testCancel(t, false)
263 }
264
265
266 func TestIgnore(t *testing.T) {
267 testCancel(t, true)
268 }
269
270
271 func TestIgnored(t *testing.T) {
272
273 c := make(chan os.Signal, 1)
274 Notify(c, syscall.SIGWINCH)
275
276
277 if Ignored(syscall.SIGWINCH) {
278 t.Errorf("expected SIGWINCH to not be ignored.")
279 }
280 Stop(c)
281 Ignore(syscall.SIGWINCH)
282
283
284 if !Ignored(syscall.SIGWINCH) {
285 t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.")
286 }
287
288 Reset()
289 }
290
291 var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.")
292
293
294 func TestDetectNohup(t *testing.T) {
295 if *checkSighupIgnored {
296 if !Ignored(syscall.SIGHUP) {
297 t.Fatal("SIGHUP is not ignored.")
298 } else {
299 t.Log("SIGHUP is ignored.")
300 }
301 } else {
302 defer Reset()
303
304
305
306 c := make(chan os.Signal, 1)
307 Notify(c, syscall.SIGHUP)
308 if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil {
309 t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out)
310 }
311 Stop(c)
312
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 := exec.Command("/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 t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data)
325 }
326 }
327 }
328
329 var (
330 sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
331 dieFromSighup = flag.Bool("die_from_sighup", false, "wait to die from uncaught SIGHUP")
332 )
333
334
335 func TestStop(t *testing.T) {
336 sigs := []syscall.Signal{
337 syscall.SIGWINCH,
338 syscall.SIGHUP,
339 syscall.SIGUSR1,
340 }
341
342 for _, sig := range sigs {
343 sig := sig
344 t.Run(fmt.Sprint(sig), func(t *testing.T) {
345
346
347
348
349 t.Parallel()
350
351
352
353
354
355 mayHaveBlockedSignal := false
356 if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) {
357 syscall.Kill(syscall.Getpid(), sig)
358 quiesce()
359
360
361
362 mayHaveBlockedSignal = true
363 }
364
365
366 c := make(chan os.Signal, 1)
367 Notify(c, sig)
368
369
370 syscall.Kill(syscall.Getpid(), sig)
371 waitSig(t, c, sig)
372
373 if mayHaveBlockedSignal {
374
375
376
377
378 quiesce()
379 select {
380 case <-c:
381 default:
382 }
383 }
384
385
386
387 Stop(c)
388 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
389 syscall.Kill(syscall.Getpid(), sig)
390 quiesce()
391
392 select {
393 case s := <-c:
394 t.Errorf("unexpected signal %v", s)
395 default:
396
397 }
398
399
400
401
402 Notify(c, sig)
403 quiesce()
404 Stop(c)
405 }
406 })
407 }
408 }
409
410
411 func TestNohup(t *testing.T) {
412
413
414
415 c := make(chan os.Signal, 1)
416 Notify(c, syscall.SIGHUP)
417
418
419
420
421
422
423
424
425
426
427
428
429 var subTimeout time.Duration
430
431 var wg sync.WaitGroup
432 wg.Add(2)
433 if deadline, ok := t.Deadline(); ok {
434 subTimeout = time.Until(deadline)
435 subTimeout -= subTimeout / 10
436 }
437 for i := 1; i <= 2; i++ {
438 i := i
439 go t.Run(fmt.Sprintf("uncaught-%d", i), func(t *testing.T) {
440 defer wg.Done()
441
442 args := []string{
443 "-test.v",
444 "-test.run=TestStop",
445 "-send_uncaught_sighup=" + strconv.Itoa(i),
446 "-die_from_sighup",
447 }
448 if subTimeout != 0 {
449 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
450 }
451 out, err := exec.Command(os.Args[0], args...).CombinedOutput()
452
453 if err == nil {
454 t.Errorf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
455 } else {
456 t.Logf("test with -send_uncaught_sighup=%d failed as expected.\nError: %v\nOutput:\n%s", i, err, out)
457 }
458 })
459 }
460 wg.Wait()
461
462 Stop(c)
463
464
465
466 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
467 t.Skip("Skipping nohup test due to running in tmux on darwin")
468 }
469
470
471 _, err := exec.LookPath("nohup")
472 if err != nil {
473 t.Skip("cannot find nohup; skipping second half of test")
474 }
475
476 wg.Add(2)
477 if deadline, ok := t.Deadline(); ok {
478 subTimeout = time.Until(deadline)
479 subTimeout -= subTimeout / 10
480 }
481 for i := 1; i <= 2; i++ {
482 i := i
483 go t.Run(fmt.Sprintf("nohup-%d", i), func(t *testing.T) {
484 defer wg.Done()
485
486
487
488
489
490
491
492 args := []string{
493 os.Args[0],
494 "-test.v",
495 "-test.run=TestStop",
496 "-send_uncaught_sighup=" + strconv.Itoa(i),
497 }
498 if subTimeout != 0 {
499 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
500 }
501 out, err := exec.Command("nohup", args...).CombinedOutput()
502
503 if err != nil {
504 t.Errorf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s", i, err, out)
505 } else {
506 t.Logf("ran test with -send_uncaught_sighup=%d under nohup.\nOutput:\n%s", i, out)
507 }
508 })
509 }
510 wg.Wait()
511 }
512
513
514 func TestSIGCONT(t *testing.T) {
515 c := make(chan os.Signal, 1)
516 Notify(c, syscall.SIGCONT)
517 defer Stop(c)
518 syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
519 waitSig(t, c, syscall.SIGCONT)
520 }
521
522
523 func TestAtomicStop(t *testing.T) {
524 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
525 atomicStopTestProgram(t)
526 t.Fatal("atomicStopTestProgram returned")
527 }
528
529 testenv.MustHaveExec(t)
530
531
532
533
534
535
536
537
538
539
540 cs := make(chan os.Signal, 1)
541 Notify(cs, syscall.SIGINT)
542 defer Stop(cs)
543
544 const execs = 10
545 for i := 0; i < execs; i++ {
546 timeout := "0"
547 if deadline, ok := t.Deadline(); ok {
548 timeout = time.Until(deadline).String()
549 }
550 cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout)
551 cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
552 out, err := cmd.CombinedOutput()
553 if err == nil {
554 if len(out) > 0 {
555 t.Logf("iteration %d: output %s", i, out)
556 }
557 } else {
558 t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
559 }
560
561 lost := bytes.Contains(out, []byte("lost signal"))
562 if lost {
563 t.Errorf("iteration %d: lost signal", i)
564 }
565
566
567
568 if err == nil {
569 if len(out) > 0 && !lost {
570 t.Errorf("iteration %d: unexpected output", i)
571 }
572 } else {
573 if ee, ok := err.(*exec.ExitError); !ok {
574 t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err)
575 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
576 t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys())
577 } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT {
578 t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee)
579 }
580 }
581 }
582 }
583
584
585
586
587 func atomicStopTestProgram(t *testing.T) {
588
589 if Ignored(syscall.SIGINT) {
590 fmt.Println("SIGINT is ignored")
591 os.Exit(1)
592 }
593
594 const tries = 10
595
596 timeout := 2 * time.Second
597 if deadline, ok := t.Deadline(); ok {
598
599
600 timeout = time.Until(deadline) / (tries + 1)
601 }
602
603 pid := syscall.Getpid()
604 printed := false
605 for i := 0; i < tries; i++ {
606 cs := make(chan os.Signal, 1)
607 Notify(cs, syscall.SIGINT)
608
609 var wg sync.WaitGroup
610 wg.Add(1)
611 go func() {
612 defer wg.Done()
613 Stop(cs)
614 }()
615
616 syscall.Kill(pid, syscall.SIGINT)
617
618
619
620
621
622
623 select {
624 case <-cs:
625 case <-time.After(timeout):
626 if !printed {
627 fmt.Print("lost signal on tries:")
628 printed = true
629 }
630 fmt.Printf(" %d", i)
631 }
632
633 wg.Wait()
634 }
635 if printed {
636 fmt.Print("\n")
637 }
638
639 os.Exit(0)
640 }
641
642 func TestTime(t *testing.T) {
643
644
645 dur := 3 * time.Second
646 if testing.Short() {
647 dur = 100 * time.Millisecond
648 }
649 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
650
651 sig := make(chan os.Signal, 1)
652 Notify(sig, syscall.SIGUSR1)
653
654 stop := make(chan struct{})
655 go func() {
656 for {
657 select {
658 case <-stop:
659
660
661 quiesce()
662 Stop(sig)
663
664
665
666
667 close(sig)
668 return
669
670 default:
671 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
672 runtime.Gosched()
673 }
674 }
675 }()
676
677 done := make(chan struct{})
678 go func() {
679 for range sig {
680
681 }
682 close(done)
683 }()
684
685 t0 := time.Now()
686 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
687 }
688
689 close(stop)
690 <-done
691 }
692
693 var (
694 checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.")
695 ctxNotifyTimes = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received")
696 )
697
698 func TestNotifyContextNotifications(t *testing.T) {
699 if *checkNotifyContext {
700 ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
701
702
703 var wg sync.WaitGroup
704 n := *ctxNotifyTimes
705 wg.Add(n)
706 for i := 0; i < n; i++ {
707 go func() {
708 syscall.Kill(syscall.Getpid(), syscall.SIGINT)
709 wg.Done()
710 }()
711 }
712 wg.Wait()
713 <-ctx.Done()
714 fmt.Print("received SIGINT")
715
716
717
718 time.Sleep(settleTime)
719 return
720 }
721
722 t.Parallel()
723 testCases := []struct {
724 name string
725 n int
726 }{
727 {"once", 1},
728 {"multiple", 10},
729 }
730 for _, tc := range testCases {
731 t.Run(tc.name, func(t *testing.T) {
732 var subTimeout time.Duration
733 if deadline, ok := t.Deadline(); ok {
734 subTimeout := time.Until(deadline)
735 subTimeout -= subTimeout / 10
736 }
737
738 args := []string{
739 "-test.v",
740 "-test.run=TestNotifyContextNotifications$",
741 "-check_notify_ctx",
742 fmt.Sprintf("-ctx_notify_times=%d", tc.n),
743 }
744 if subTimeout != 0 {
745 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
746 }
747 out, err := exec.Command(os.Args[0], args...).CombinedOutput()
748 if err != nil {
749 t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
750 }
751 if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
752 t.Errorf("got %q, wanted %q", out, want)
753 }
754 })
755 }
756 }
757
758 func TestNotifyContextStop(t *testing.T) {
759 Ignore(syscall.SIGHUP)
760 if !Ignored(syscall.SIGHUP) {
761 t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.")
762 }
763
764 parent, cancelParent := context.WithCancel(context.Background())
765 defer cancelParent()
766 c, stop := NotifyContext(parent, syscall.SIGHUP)
767 defer stop()
768
769
770 if Ignored(syscall.SIGHUP) {
771 t.Errorf("expected SIGHUP to not be ignored.")
772 }
773
774 if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got {
775 t.Errorf("c.String() = %q, wanted %q", got, want)
776 }
777
778 stop()
779 select {
780 case <-c.Done():
781 if got := c.Err(); got != context.Canceled {
782 t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
783 }
784 case <-time.After(time.Second):
785 t.Errorf("timed out waiting for context to be done after calling stop")
786 }
787 }
788
789 func TestNotifyContextCancelParent(t *testing.T) {
790 parent, cancelParent := context.WithCancel(context.Background())
791 defer cancelParent()
792 c, stop := NotifyContext(parent, syscall.SIGINT)
793 defer stop()
794
795 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
796 t.Errorf("c.String() = %q, want %q", got, want)
797 }
798
799 cancelParent()
800 select {
801 case <-c.Done():
802 if got := c.Err(); got != context.Canceled {
803 t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
804 }
805 case <-time.After(time.Second):
806 t.Errorf("timed out waiting for parent context to be canceled")
807 }
808 }
809
810 func TestNotifyContextPrematureCancelParent(t *testing.T) {
811 parent, cancelParent := context.WithCancel(context.Background())
812 defer cancelParent()
813
814 cancelParent()
815 c, stop := NotifyContext(parent, syscall.SIGINT)
816 defer stop()
817
818 if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
819 t.Errorf("c.String() = %q, want %q", got, want)
820 }
821
822 select {
823 case <-c.Done():
824 if got := c.Err(); got != context.Canceled {
825 t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
826 }
827 case <-time.After(time.Second):
828 t.Errorf("timed out waiting for parent context to be canceled")
829 }
830 }
831
832 func TestNotifyContextSimultaneousStop(t *testing.T) {
833 c, stop := NotifyContext(context.Background(), syscall.SIGINT)
834 defer stop()
835
836 if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
837 t.Errorf("c.String() = %q, want %q", got, want)
838 }
839
840 var wg sync.WaitGroup
841 n := 10
842 wg.Add(n)
843 for i := 0; i < n; i++ {
844 go func() {
845 stop()
846 wg.Done()
847 }()
848 }
849 wg.Wait()
850 select {
851 case <-c.Done():
852 if got := c.Err(); got != context.Canceled {
853 t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
854 }
855 case <-time.After(time.Second):
856 t.Errorf("expected context to be canceled")
857 }
858 }
859
860 func TestNotifyContextStringer(t *testing.T) {
861 parent, cancelParent := context.WithCancel(context.Background())
862 defer cancelParent()
863 c, stop := NotifyContext(parent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
864 defer stop()
865
866 want := `signal.NotifyContext(context.Background.WithCancel, [hangup interrupt terminated])`
867 if got := fmt.Sprint(c); got != want {
868 t.Errorf("c.String() = %q, want %q", got, want)
869 }
870 }
871
872
873 func TestSignalTrace(t *testing.T) {
874 done := make(chan struct{})
875 quit := make(chan struct{})
876 c := make(chan os.Signal, 1)
877 Notify(c, syscall.SIGHUP)
878
879
880
881
882 go func() {
883 defer close(done)
884 defer Stop(c)
885 pid := syscall.Getpid()
886 for {
887 select {
888 case <-quit:
889 return
890 default:
891 syscall.Kill(pid, syscall.SIGHUP)
892 }
893 waitSig(t, c, syscall.SIGHUP)
894 }
895 }()
896
897 for i := 0; i < 100; i++ {
898 buf := new(bytes.Buffer)
899 if err := trace.Start(buf); err != nil {
900 t.Fatalf("[%d] failed to start tracing: %v", i, err)
901 }
902 time.After(1 * time.Microsecond)
903 trace.Stop()
904 size := buf.Len()
905 if size == 0 {
906 t.Fatalf("[%d] trace is empty", i)
907 }
908 }
909 close(quit)
910 <-done
911 }
912
View as plain text