Source file
src/os/signal/signal_test.go
1
2
3
4
5
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
28
29
30
31
32 var settleTime = 100 * time.Millisecond
33
34
35
36
37 var fatalWaitingTime = 30 * time.Second
38
39 func init() {
40 if testenv.Builder() == "solaris-amd64-oraclerel" {
41
42
43
44
45
46
47
48
49
50
51
52
53
54 settleTime = 11 * time.Second
55 } else if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "ppc64") {
56
57
58
59
60
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)
65 }
66 }
67 }
68
69 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
70 t.Helper()
71 waitSig1(t, c, sig, false)
72 }
73 func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
74 t.Helper()
75 waitSig1(t, c, sig, true)
76 }
77
78 func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
79 t.Helper()
80
81
82
83 start := time.Now()
84 timer := time.NewTimer(settleTime / 10)
85 defer timer.Stop()
86
87
88
89
90
91 for time.Since(start) < fatalWaitingTime {
92 select {
93 case s := <-c:
94 if s == sig {
95 return
96 }
97 if !all || s != syscall.SIGURG {
98 t.Fatalf("signal was %v, want %v", s, sig)
99 }
100 case <-timer.C:
101 timer.Reset(settleTime / 10)
102 }
103 }
104 t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig)
105 }
106
107
108
109 func quiesce() {
110
111
112
113
114
115
116 start := time.Now()
117 for time.Since(start) < settleTime {
118 time.Sleep(settleTime / 10)
119 }
120 }
121
122
123 func TestSignal(t *testing.T) {
124
125 c := make(chan os.Signal, 1)
126 Notify(c, syscall.SIGHUP)
127 defer Stop(c)
128
129
130 t.Logf("sighup...")
131 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
132 waitSig(t, c, syscall.SIGHUP)
133
134
135
136
137 c1 := make(chan os.Signal, 10)
138 Notify(c1)
139
140 Reset(syscall.SIGURG)
141 defer Stop(c1)
142
143
144 t.Logf("sigwinch...")
145 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
146 waitSigAll(t, c1, syscall.SIGWINCH)
147
148
149
150
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)
157
158
159 waitSig(t, c, syscall.SIGHUP)
160 }
161
162 func TestStress(t *testing.T) {
163 dur := 3 * time.Second
164 if testing.Short() {
165 dur = 100 * time.Millisecond
166 }
167 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
168
169 sig := make(chan os.Signal, 1)
170 Notify(sig, syscall.SIGUSR1)
171
172 go func() {
173 stop := time.After(dur)
174 for {
175 select {
176 case <-stop:
177
178
179 quiesce()
180 Stop(sig)
181
182
183
184
185 close(sig)
186 return
187
188 default:
189 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
190 runtime.Gosched()
191 }
192 }
193 }()
194
195 for range sig {
196
197 }
198 }
199
200 func testCancel(t *testing.T, ignore bool) {
201
202 c1 := make(chan os.Signal, 1)
203 Notify(c1, syscall.SIGWINCH)
204 defer Stop(c1)
205
206
207 c2 := make(chan os.Signal, 1)
208 Notify(c2, syscall.SIGHUP)
209 defer Stop(c2)
210
211
212 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
213 waitSig(t, c1, syscall.SIGWINCH)
214
215
216 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
217 waitSig(t, c2, syscall.SIGHUP)
218
219
220
221 if ignore {
222 Ignore(syscall.SIGWINCH, syscall.SIGHUP)
223
224
225
226 } else {
227 Reset(syscall.SIGWINCH, syscall.SIGHUP)
228 }
229
230
231 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
232
233
234 if ignore {
235 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
236 }
237
238 quiesce()
239
240 select {
241 case s := <-c1:
242 t.Errorf("unexpected signal %v", s)
243 default:
244
245 }
246
247 select {
248 case s := <-c2:
249 t.Errorf("unexpected signal %v", s)
250 default:
251
252 }
253
254
255
256
257 Notify(c1, syscall.SIGWINCH)
258 Notify(c2, syscall.SIGHUP)
259 quiesce()
260 }
261
262
263 func TestReset(t *testing.T) {
264 testCancel(t, false)
265 }
266
267
268 func TestIgnore(t *testing.T) {
269 testCancel(t, true)
270 }
271
272
273 func TestIgnored(t *testing.T) {
274
275 c := make(chan os.Signal, 1)
276 Notify(c, syscall.SIGWINCH)
277
278
279 if Ignored(syscall.SIGWINCH) {
280 t.Errorf("expected SIGWINCH to not be ignored.")
281 }
282 Stop(c)
283 Ignore(syscall.SIGWINCH)
284
285
286 if !Ignored(syscall.SIGWINCH) {
287 t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.")
288 }
289
290 Reset()
291 }
292
293 var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.")
294
295
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.")
302 }
303 } else {
304 defer Reset()
305
306
307
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)
312 }
313 Stop(c)
314
315 _, err := os.Stat("/usr/bin/nohup")
316 if err != nil {
317 t.Skip("cannot find nohup; skipping second half of test")
318 }
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()
322
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)
327 }
328 }
329 }
330
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")
334 )
335
336
337 func TestStop(t *testing.T) {
338 sigs := []syscall.Signal{
339 syscall.SIGWINCH,
340 syscall.SIGHUP,
341 syscall.SIGUSR1,
342 }
343
344 for _, sig := range sigs {
345 sig := sig
346 t.Run(fmt.Sprint(sig), func(t *testing.T) {
347
348
349
350
351 t.Parallel()
352
353
354
355
356
357 mayHaveBlockedSignal := false
358 if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) {
359 syscall.Kill(syscall.Getpid(), sig)
360 quiesce()
361
362
363
364 mayHaveBlockedSignal = true
365 }
366
367
368 c := make(chan os.Signal, 1)
369 Notify(c, sig)
370
371
372 syscall.Kill(syscall.Getpid(), sig)
373 waitSig(t, c, sig)
374
375 if mayHaveBlockedSignal {
376
377
378
379
380 quiesce()
381 select {
382 case <-c:
383 default:
384 }
385 }
386
387
388
389 Stop(c)
390 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
391 syscall.Kill(syscall.Getpid(), sig)
392 quiesce()
393
394 select {
395 case s := <-c:
396 t.Errorf("unexpected signal %v", s)
397 default:
398
399 }
400
401
402
403
404 Notify(c, sig)
405 quiesce()
406 Stop(c)
407 }
408 })
409 }
410 }
411
412
413 func TestNohup(t *testing.T) {
414
415
416
417 c := make(chan os.Signal, 1)
418 Notify(c, syscall.SIGHUP)
419
420
421
422
423
424
425
426
427
428
429
430
431 var subTimeout time.Duration
432
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
438 }
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()
443
444 args := []string{
445 "-test.v",
446 "-test.run=TestStop",
447 "-send_uncaught_sighup=" + strconv.Itoa(i),
448 "-die_from_sighup",
449 }
450 if subTimeout != 0 {
451 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
452 }
453 out, err := exec.Command(os.Args[0], args...).CombinedOutput()
454
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)
459 }
460 })
461 }
462 wg.Wait()
463
464 Stop(c)
465
466
467
468 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
469 t.Skip("Skipping nohup test due to running in tmux on darwin")
470 }
471
472
473 _, err := exec.LookPath("nohup")
474 if err != nil {
475 t.Skip("cannot find nohup; skipping second half of test")
476 }
477
478 wg.Add(2)
479 if deadline, ok := t.Deadline(); ok {
480 subTimeout = time.Until(deadline)
481 subTimeout -= subTimeout / 10
482 }
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()
487
488
489
490
491
492
493
494 args := []string{
495 os.Args[0],
496 "-test.v",
497 "-test.run=TestStop",
498 "-send_uncaught_sighup=" + strconv.Itoa(i),
499 }
500 if subTimeout != 0 {
501 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
502 }
503 out, err := exec.Command("nohup", args...).CombinedOutput()
504
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)
509 }
510 })
511 }
512 wg.Wait()
513 }
514
515
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)
522 }
523
524
525 func TestAtomicStop(t *testing.T) {
526 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
527 atomicStopTestProgram(t)
528 t.Fatal("atomicStopTestProgram returned")
529 }
530
531 testenv.MustHaveExec(t)
532
533
534
535
536
537
538
539
540
541
542 cs := make(chan os.Signal, 1)
543 Notify(cs, syscall.SIGINT)
544 defer Stop(cs)
545
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()
551 }
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)
558 }
559 } else {
560 t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
561 }
562
563 lost := bytes.Contains(out, []byte("lost signal"))
564 if lost {
565 t.Errorf("iteration %d: lost signal", i)
566 }
567
568
569
570 if err == nil {
571 if len(out) > 0 && !lost {
572 t.Errorf("iteration %d: unexpected output", i)
573 }
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)
581 }
582 }
583 }
584 }
585
586
587
588
589 func atomicStopTestProgram(t *testing.T) {
590
591 if Ignored(syscall.SIGINT) {
592 fmt.Println("SIGINT is ignored")
593 os.Exit(1)
594 }
595
596 const tries = 10
597
598 timeout := 2 * time.Second
599 if deadline, ok := t.Deadline(); ok {
600
601
602 timeout = time.Until(deadline) / (tries + 1)
603 }
604
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)
610
611 var wg sync.WaitGroup
612 wg.Add(1)
613 go func() {
614 defer wg.Done()
615 Stop(cs)
616 }()
617
618 syscall.Kill(pid, syscall.SIGINT)
619
620
621
622
623
624
625 select {
626 case <-cs:
627 case <-time.After(timeout):
628 if !printed {
629 fmt.Print("lost signal on tries:")
630 printed = true
631 }
632 fmt.Printf(" %d", i)
633 }
634
635 wg.Wait()
636 }
637 if printed {
638 fmt.Print("\n")
639 }
640
641 os.Exit(0)
642 }
643
644 func TestTime(t *testing.T) {
645
646
647 dur := 3 * time.Second
648 if testing.Short() {
649 dur = 100 * time.Millisecond
650 }
651 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
652
653 sig := make(chan os.Signal, 1)
654 Notify(sig, syscall.SIGUSR1)
655
656 stop := make(chan struct{})
657 go func() {
658 for {
659 select {
660 case <-stop:
661
662
663 quiesce()
664 Stop(sig)
665
666
667
668
669 close(sig)
670 return
671
672 default:
673 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
674 runtime.Gosched()
675 }
676 }
677 }()
678
679 done := make(chan struct{})
680 go func() {
681 for range sig {
682
683 }
684 close(done)
685 }()
686
687 t0 := time.Now()
688 for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
689 }
690
691 close(stop)
692 <-done
693 }
694
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")
698 )
699
700 func TestNotifyContextNotifications(t *testing.T) {
701 if *checkNotifyContext {
702 ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
703
704
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()
712 }()
713 }
714 wg.Wait()
715 <-ctx.Done()
716 fmt.Print("received SIGINT")
717
718
719
720 time.Sleep(settleTime)
721 return
722 }
723
724 t.Parallel()
725 testCases := []struct {
726 name string
727 n int
728 }{
729 {"once", 1},
730 {"multiple", 10},
731 }
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
738 }
739
740 args := []string{
741 "-test.v",
742 "-test.run=TestNotifyContextNotifications$",
743 "-check_notify_ctx",
744 fmt.Sprintf("-ctx_notify_times=%d", tc.n),
745 }
746 if subTimeout != 0 {
747 args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
748 }
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)
752 }
753 if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
754 t.Errorf("got %q, wanted %q", out, want)
755 }
756 })
757 }
758 }
759
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.")
764 }
765
766 parent, cancelParent := context.WithCancel(context.Background())
767 defer cancelParent()
768 c, stop := NotifyContext(parent, syscall.SIGHUP)
769 defer stop()
770
771
772 if Ignored(syscall.SIGHUP) {
773 t.Errorf("expected SIGHUP to not be ignored.")
774 }
775
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)
778 }
779
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)
785 }
786 case <-time.After(time.Second):
787 t.Errorf("timed out waiting for context to be done after calling stop")
788 }
789 }
790
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()
796
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)
799 }
800
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)
806 }
807 case <-time.After(time.Second):
808 t.Errorf("timed out waiting for parent context to be canceled")
809 }
810 }
811
812 func TestNotifyContextPrematureCancelParent(t *testing.T) {
813 parent, cancelParent := context.WithCancel(context.Background())
814 defer cancelParent()
815
816 cancelParent()
817 c, stop := NotifyContext(parent, syscall.SIGINT)
818 defer stop()
819
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)
822 }
823
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)
828 }
829 case <-time.After(time.Second):
830 t.Errorf("timed out waiting for parent context to be canceled")
831 }
832 }
833
834 func TestNotifyContextSimultaneousStop(t *testing.T) {
835 c, stop := NotifyContext(context.Background(), syscall.SIGINT)
836 defer stop()
837
838 if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
839 t.Errorf("c.String() = %q, want %q", got, want)
840 }
841
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()
849 }()
850 }
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)
856 }
857 case <-time.After(time.Second):
858 t.Errorf("expected context to be canceled")
859 }
860 }
861
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()
867
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)
871 }
872 }
873
874
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)
880
881
882
883
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)
894 }
895 waitSig(t, c, syscall.SIGHUP)
896 }
897 }()
898
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)
903 }
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)
909 }
910 }
911 close(quit)
912 <-done
913 }
914
View as plain text