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