blob: e4df8af816e0c2c8b1dc02bb92ed14e0d22ed94e [file] [log] [blame]
Russ Cox35586f72012-02-13 13:52:37 -05001// 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
Aram Hăvărneanu6d0d08b2014-01-10 02:49:37 +11005// +build darwin dragonfly freebsd linux netbsd openbsd solaris
Russ Cox35586f72012-02-13 13:52:37 -05006
Ian Lance Taylor5bcfd882017-07-13 08:10:43 -07007package signal
Russ Cox35586f72012-02-13 13:52:37 -05008
9import (
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -070010 "bytes"
Russ Coxcb4428e2013-03-15 00:00:02 -040011 "flag"
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -070012 "fmt"
Ian Lance Taylorb4dd1d92017-06-24 20:54:47 -070013 "internal/testenv"
Russ Coxcb4428e2013-03-15 00:00:02 -040014 "io/ioutil"
Russ Cox35586f72012-02-13 13:52:37 -050015 "os"
Russ Coxcb4428e2013-03-15 00:00:02 -040016 "os/exec"
Dmitriy Vyukov91484c62012-12-28 15:36:06 +040017 "runtime"
Russ Coxcb4428e2013-03-15 00:00:02 -040018 "strconv"
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -070019 "sync"
Russ Cox35586f72012-02-13 13:52:37 -050020 "syscall"
21 "testing"
22 "time"
23)
24
Russ Cox35586f72012-02-13 13:52:37 -050025func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
26 select {
27 case s := <-c:
28 if s != sig {
29 t.Fatalf("signal was %v, want %v", s, sig)
30 }
31 case <-time.After(1 * time.Second):
32 t.Fatalf("timeout waiting for %v", sig)
33 }
34}
35
Russ Coxcb4428e2013-03-15 00:00:02 -040036// Test that basic signal handling works.
Russ Cox35586f72012-02-13 13:52:37 -050037func TestSignal(t *testing.T) {
38 // Ask for SIGHUP
39 c := make(chan os.Signal, 1)
Russ Coxcb4428e2013-03-15 00:00:02 -040040 Notify(c, syscall.SIGHUP)
41 defer Stop(c)
Russ Cox35586f72012-02-13 13:52:37 -050042
Russ Cox35586f72012-02-13 13:52:37 -050043 // Send this process a SIGHUP
Joel Sing932428a2013-10-07 09:04:20 -070044 t.Logf("sighup...")
Russ Coxcb4428e2013-03-15 00:00:02 -040045 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
46 waitSig(t, c, syscall.SIGHUP)
Russ Cox35586f72012-02-13 13:52:37 -050047
48 // Ask for everything we can get.
49 c1 := make(chan os.Signal, 1)
50 Notify(c1)
51
Russ Cox35586f72012-02-13 13:52:37 -050052 // Send this process a SIGWINCH
Joel Sing932428a2013-10-07 09:04:20 -070053 t.Logf("sigwinch...")
Russ Cox35586f72012-02-13 13:52:37 -050054 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
55 waitSig(t, c1, syscall.SIGWINCH)
56
57 // Send two more SIGHUPs, to make sure that
58 // they get delivered on c1 and that not reading
59 // from c does not block everything.
Joel Sing932428a2013-10-07 09:04:20 -070060 t.Logf("sighup...")
Russ Cox35586f72012-02-13 13:52:37 -050061 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
62 waitSig(t, c1, syscall.SIGHUP)
Joel Sing932428a2013-10-07 09:04:20 -070063 t.Logf("sighup...")
Russ Cox35586f72012-02-13 13:52:37 -050064 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
65 waitSig(t, c1, syscall.SIGHUP)
66
67 // The first SIGHUP should be waiting for us on c.
68 waitSig(t, c, syscall.SIGHUP)
69}
Dmitriy Vyukov91484c62012-12-28 15:36:06 +040070
71func TestStress(t *testing.T) {
72 dur := 3 * time.Second
73 if testing.Short() {
74 dur = 100 * time.Millisecond
75 }
76 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
77 done := make(chan bool)
78 finished := make(chan bool)
79 go func() {
80 sig := make(chan os.Signal, 1)
81 Notify(sig, syscall.SIGUSR1)
Russ Coxcb4428e2013-03-15 00:00:02 -040082 defer Stop(sig)
Dmitriy Vyukov91484c62012-12-28 15:36:06 +040083 Loop:
84 for {
85 select {
86 case <-sig:
87 case <-done:
88 break Loop
89 }
90 }
91 finished <- true
92 }()
93 go func() {
94 Loop:
95 for {
96 select {
97 case <-done:
98 break Loop
99 default:
100 syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
101 runtime.Gosched()
102 }
103 }
104 finished <- true
105 }()
106 time.Sleep(dur)
107 close(done)
108 <-finished
109 <-finished
Dmitriy Vyukovfe017552013-03-11 22:31:34 +0400110 // When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
111 // into subsequent TestSignal() causing failure.
112 // Sleep for a while to reduce the possibility of the failure.
113 time.Sleep(10 * time.Millisecond)
Dmitriy Vyukov91484c62012-12-28 15:36:06 +0400114}
Russ Coxcb4428e2013-03-15 00:00:02 -0400115
Michael MacInnis194ad162015-01-29 22:37:41 -0500116func testCancel(t *testing.T, ignore bool) {
117 // Send SIGWINCH. By default this signal should be ignored.
118 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
119 time.Sleep(100 * time.Millisecond)
120
121 // Ask to be notified on c1 when a SIGWINCH is received.
122 c1 := make(chan os.Signal, 1)
123 Notify(c1, syscall.SIGWINCH)
124 defer Stop(c1)
125
126 // Ask to be notified on c2 when a SIGHUP is received.
127 c2 := make(chan os.Signal, 1)
128 Notify(c2, syscall.SIGHUP)
129 defer Stop(c2)
130
131 // Send this process a SIGWINCH and wait for notification on c1.
132 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
133 waitSig(t, c1, syscall.SIGWINCH)
134
135 // Send this process a SIGHUP and wait for notification on c2.
136 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
137 waitSig(t, c2, syscall.SIGHUP)
138
139 // Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
140 if ignore {
141 Ignore(syscall.SIGWINCH, syscall.SIGHUP)
142 } else {
143 Reset(syscall.SIGWINCH, syscall.SIGHUP)
144 }
145
Ian Lance Taylorbc4fdfd2016-06-01 09:31:31 -0700146 // At this point we do not expect any further signals on c1.
147 // However, it is just barely possible that the initial SIGWINCH
148 // at the start of this function was delivered after we called
149 // Notify on c1. In that case the waitSig for SIGWINCH may have
150 // picked up that initial SIGWINCH, and the second SIGWINCH may
151 // then have been delivered on the channel. This sequence of events
152 // may have caused issue 15661.
153 // So, read any possible signal from the channel now.
154 select {
155 case <-c1:
156 default:
157 }
158
Michael MacInnis194ad162015-01-29 22:37:41 -0500159 // Send this process a SIGWINCH. It should be ignored.
160 syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
161
162 // If ignoring, Send this process a SIGHUP. It should be ignored.
163 if ignore {
164 syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
165 }
166
167 select {
168 case s := <-c1:
169 t.Fatalf("unexpected signal %v", s)
170 case <-time.After(100 * time.Millisecond):
171 // nothing to read - good
172 }
173
174 select {
175 case s := <-c2:
176 t.Fatalf("unexpected signal %v", s)
177 case <-time.After(100 * time.Millisecond):
178 // nothing to read - good
179 }
180
181 // Reset the signal handlers for all signals.
182 Reset()
183}
184
185// Test that Reset cancels registration for listed signals on all channels.
186func TestReset(t *testing.T) {
187 testCancel(t, false)
188}
189
190// Test that Ignore cancels registration for listed signals on all channels.
191func TestIgnore(t *testing.T) {
192 testCancel(t, true)
193}
194
Russ Coxcb4428e2013-03-15 00:00:02 -0400195var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
196
197// Test that Stop cancels the channel's registrations.
198func TestStop(t *testing.T) {
199 sigs := []syscall.Signal{
200 syscall.SIGWINCH,
201 syscall.SIGHUP,
Ian Lance Taylore24b2442016-01-04 16:19:38 -0800202 syscall.SIGUSR1,
Russ Coxcb4428e2013-03-15 00:00:02 -0400203 }
204
205 for _, sig := range sigs {
206 // Send the signal.
207 // If it's SIGWINCH, we should not see it.
208 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
Ian Lance Taylore24b2442016-01-04 16:19:38 -0800209 if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
Russ Coxcb4428e2013-03-15 00:00:02 -0400210 syscall.Kill(syscall.Getpid(), sig)
211 }
Russ Coxc1e33202014-09-16 15:26:00 -0400212 time.Sleep(100 * time.Millisecond)
Russ Coxcb4428e2013-03-15 00:00:02 -0400213
214 // Ask for signal
215 c := make(chan os.Signal, 1)
216 Notify(c, sig)
217 defer Stop(c)
218
219 // Send this process that signal
220 syscall.Kill(syscall.Getpid(), sig)
221 waitSig(t, c, sig)
222
223 Stop(c)
224 select {
225 case s := <-c:
226 t.Fatalf("unexpected signal %v", s)
Russ Coxc1e33202014-09-16 15:26:00 -0400227 case <-time.After(100 * time.Millisecond):
Russ Coxcb4428e2013-03-15 00:00:02 -0400228 // nothing to read - good
229 }
230
231 // Send the signal.
232 // If it's SIGWINCH, we should not see it.
233 // If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
234 if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
235 syscall.Kill(syscall.Getpid(), sig)
236 }
237
238 select {
239 case s := <-c:
240 t.Fatalf("unexpected signal %v", s)
Russ Coxc1e33202014-09-16 15:26:00 -0400241 case <-time.After(100 * time.Millisecond):
Russ Coxcb4428e2013-03-15 00:00:02 -0400242 // nothing to read - good
243 }
244 }
245}
246
247// Test that when run under nohup, an uncaught SIGHUP does not kill the program,
248// but a
249func TestNohup(t *testing.T) {
250 // Ugly: ask for SIGHUP so that child will not have no-hup set
251 // even if test is running under nohup environment.
252 // We have no intention of reading from c.
253 c := make(chan os.Signal, 1)
254 Notify(c, syscall.SIGHUP)
255
256 // When run without nohup, the test should crash on an uncaught SIGHUP.
257 // When run under nohup, the test should ignore uncaught SIGHUPs,
258 // because the runtime is not supposed to be listening for them.
259 // Either way, TestStop should still be able to catch them when it wants them
260 // and then when it stops wanting them, the original behavior should resume.
261 //
262 // send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
263 // send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
264 //
265 // Both should fail without nohup and succeed with nohup.
266
267 for i := 1; i <= 2; i++ {
268 out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
269 if err == nil {
270 t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
271 }
272 }
273
274 Stop(c)
275
Aaron Jacobsacb47652015-08-25 08:53:42 +1000276 // Skip the nohup test below when running in tmux on darwin, since nohup
277 // doesn't work correctly there. See issue #5135.
278 if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
279 t.Skip("Skipping nohup test due to running in tmux on darwin")
280 }
281
Russ Coxcb4428e2013-03-15 00:00:02 -0400282 // Again, this time with nohup, assuming we can find it.
283 _, err := os.Stat("/usr/bin/nohup")
284 if err != nil {
285 t.Skip("cannot find nohup; skipping second half of test")
286 }
287
288 for i := 1; i <= 2; i++ {
289 os.Remove("nohup.out")
290 out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
291
292 data, _ := ioutil.ReadFile("nohup.out")
293 os.Remove("nohup.out")
294 if err != nil {
295 t.Fatalf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", i, err, out, data)
296 }
297 }
298}
Ian Lance Taylor81b35112015-12-31 12:06:31 -0800299
300// Test that SIGCONT works (issue 8953).
301func TestSIGCONT(t *testing.T) {
302 c := make(chan os.Signal, 1)
303 Notify(c, syscall.SIGCONT)
304 defer Stop(c)
305 syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
306 waitSig(t, c, syscall.SIGCONT)
307}
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -0700308
309// Test race between stopping and receiving a signal (issue 14571).
310func TestAtomicStop(t *testing.T) {
311 if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
312 atomicStopTestProgram()
313 t.Fatal("atomicStopTestProgram returned")
314 }
315
Ian Lance Taylorb4dd1d92017-06-24 20:54:47 -0700316 testenv.MustHaveExec(t)
317
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -0700318 const execs = 10
319 for i := 0; i < execs; i++ {
320 cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop")
321 cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
322 out, err := cmd.CombinedOutput()
323 if err == nil {
Ian Lance Tayloreb971602017-11-22 19:12:12 -0800324 if len(out) > 0 {
325 t.Logf("iteration %d: output %s", i, out)
326 }
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -0700327 } else {
328 t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
329 }
330
331 lost := bytes.Contains(out, []byte("lost signal"))
332 if lost {
333 t.Errorf("iteration %d: lost signal", i)
334 }
335
336 // The program should either die due to SIGINT,
337 // or exit with success without printing "lost signal".
338 if err == nil {
339 if len(out) > 0 && !lost {
340 t.Errorf("iteration %d: unexpected output", i)
341 }
342 } else {
343 if ee, ok := err.(*exec.ExitError); !ok {
344 t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err)
345 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
346 t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys())
347 } else if !ws.Signaled() || ws.Signal() != syscall.SIGINT {
348 t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee)
349 }
350 }
351 }
352}
353
354// atomicStopTestProgram is run in a subprocess by TestAtomicStop.
355// It tries to trigger a signal delivery race. This function should
356// either catch a signal or die from it.
357func atomicStopTestProgram() {
358 const tries = 10
359 pid := syscall.Getpid()
360 printed := false
361 for i := 0; i < tries; i++ {
362 cs := make(chan os.Signal, 1)
363 Notify(cs, syscall.SIGINT)
364
365 var wg sync.WaitGroup
366 wg.Add(1)
367 go func() {
368 defer wg.Done()
369 Stop(cs)
370 }()
371
372 syscall.Kill(pid, syscall.SIGINT)
373
374 // At this point we should either die from SIGINT or
375 // get a notification on cs. If neither happens, we
376 // dropped the signal. Give it a second to deliver,
377 // which is far far longer than it should require.
378
379 select {
380 case <-cs:
381 case <-time.After(1 * time.Second):
382 if !printed {
Ian Lance Tayloreb971602017-11-22 19:12:12 -0800383 fmt.Print("lost signal on tries:")
Ian Lance Taylor8ec7a392017-06-16 09:29:44 -0700384 printed = true
385 }
386 fmt.Printf(" %d", i)
387 }
388
389 wg.Wait()
390 }
391 if printed {
392 fmt.Print("\n")
393 }
394
395 os.Exit(0)
396}