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
5package signal
6
7import (
8	"internal/itoa"
9	"os"
10	"runtime"
11	"syscall"
12	"testing"
13	"time"
14)
15
16func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
17	select {
18	case s := <-c:
19		if s != sig {
20			t.Fatalf("signal was %v, want %v", s, sig)
21		}
22	case <-time.After(1 * time.Second):
23		t.Fatalf("timeout waiting for %v", sig)
24	}
25}
26
27// Test that basic signal handling works.
28func TestSignal(t *testing.T) {
29	// Ask for hangup
30	c := make(chan os.Signal, 1)
31	Notify(c, syscall.Note("hangup"))
32	defer Stop(c)
33
34	// Send this process a hangup
35	t.Logf("hangup...")
36	postNote(syscall.Getpid(), "hangup")
37	waitSig(t, c, syscall.Note("hangup"))
38
39	// Ask for everything we can get.
40	c1 := make(chan os.Signal, 1)
41	Notify(c1)
42
43	// Send this process an alarm
44	t.Logf("alarm...")
45	postNote(syscall.Getpid(), "alarm")
46	waitSig(t, c1, syscall.Note("alarm"))
47
48	// Send two more hangups, to make sure that
49	// they get delivered on c1 and that not reading
50	// from c does not block everything.
51	t.Logf("hangup...")
52	postNote(syscall.Getpid(), "hangup")
53	waitSig(t, c1, syscall.Note("hangup"))
54	t.Logf("hangup...")
55	postNote(syscall.Getpid(), "hangup")
56	waitSig(t, c1, syscall.Note("hangup"))
57
58	// The first SIGHUP should be waiting for us on c.
59	waitSig(t, c, syscall.Note("hangup"))
60}
61
62func TestStress(t *testing.T) {
63	dur := 3 * time.Second
64	if testing.Short() {
65		dur = 100 * time.Millisecond
66	}
67	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
68	done := make(chan bool)
69	finished := make(chan bool)
70	go func() {
71		sig := make(chan os.Signal, 1)
72		Notify(sig, syscall.Note("alarm"))
73		defer Stop(sig)
74	Loop:
75		for {
76			select {
77			case <-sig:
78			case <-done:
79				break Loop
80			}
81		}
82		finished <- true
83	}()
84	go func() {
85	Loop:
86		for {
87			select {
88			case <-done:
89				break Loop
90			default:
91				postNote(syscall.Getpid(), "alarm")
92				runtime.Gosched()
93			}
94		}
95		finished <- true
96	}()
97	time.Sleep(dur)
98	close(done)
99	<-finished
100	<-finished
101	// When run with 'go test -cpu=1,2,4' alarm from this test can slip
102	// into subsequent TestSignal() causing failure.
103	// Sleep for a while to reduce the possibility of the failure.
104	time.Sleep(10 * time.Millisecond)
105}
106
107// Test that Stop cancels the channel's registrations.
108func TestStop(t *testing.T) {
109	if testing.Short() {
110		t.Skip("skipping in short mode")
111	}
112	sigs := []string{
113		"alarm",
114		"hangup",
115	}
116
117	for _, sig := range sigs {
118		// Send the signal.
119		// If it's alarm, we should not see it.
120		// If it's hangup, maybe we'll die. Let the flag tell us what to do.
121		if sig != "hangup" {
122			postNote(syscall.Getpid(), sig)
123		}
124		time.Sleep(100 * time.Millisecond)
125
126		// Ask for signal
127		c := make(chan os.Signal, 1)
128		Notify(c, syscall.Note(sig))
129		defer Stop(c)
130
131		// Send this process that signal
132		postNote(syscall.Getpid(), sig)
133		waitSig(t, c, syscall.Note(sig))
134
135		Stop(c)
136		select {
137		case s := <-c:
138			t.Fatalf("unexpected signal %v", s)
139		case <-time.After(100 * time.Millisecond):
140			// nothing to read - good
141		}
142
143		// Send the signal.
144		// If it's alarm, we should not see it.
145		// If it's hangup, maybe we'll die. Let the flag tell us what to do.
146		if sig != "hangup" {
147			postNote(syscall.Getpid(), sig)
148		}
149
150		select {
151		case s := <-c:
152			t.Fatalf("unexpected signal %v", s)
153		case <-time.After(100 * time.Millisecond):
154			// nothing to read - good
155		}
156	}
157}
158
159func postNote(pid int, note string) error {
160	f, err := os.OpenFile("/proc/"+itoa.Itoa(pid)+"/note", os.O_WRONLY, 0)
161	if err != nil {
162		return err
163	}
164	defer f.Close()
165	_, err = f.Write([]byte(note))
166	return err
167}
168