Home | History | Annotate | Download | only in signal
      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 
      5 // +build darwin dragonfly freebsd linux netbsd openbsd solaris
      6 
      7 package signal
      8 
      9 import (
     10 	"flag"
     11 	"io/ioutil"
     12 	"os"
     13 	"os/exec"
     14 	"runtime"
     15 	"strconv"
     16 	"syscall"
     17 	"testing"
     18 	"time"
     19 )
     20 
     21 func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
     22 	select {
     23 	case s := <-c:
     24 		if s != sig {
     25 			t.Fatalf("signal was %v, want %v", s, sig)
     26 		}
     27 	case <-time.After(1 * time.Second):
     28 		t.Fatalf("timeout waiting for %v", sig)
     29 	}
     30 }
     31 
     32 // Test that basic signal handling works.
     33 func TestSignal(t *testing.T) {
     34 	// Ask for SIGHUP
     35 	c := make(chan os.Signal, 1)
     36 	Notify(c, syscall.SIGHUP)
     37 	defer Stop(c)
     38 
     39 	// Send this process a SIGHUP
     40 	t.Logf("sighup...")
     41 	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
     42 	waitSig(t, c, syscall.SIGHUP)
     43 
     44 	// Ask for everything we can get.
     45 	c1 := make(chan os.Signal, 1)
     46 	Notify(c1)
     47 
     48 	// Send this process a SIGWINCH
     49 	t.Logf("sigwinch...")
     50 	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
     51 	waitSig(t, c1, syscall.SIGWINCH)
     52 
     53 	// Send two more SIGHUPs, to make sure that
     54 	// they get delivered on c1 and that not reading
     55 	// from c does not block everything.
     56 	t.Logf("sighup...")
     57 	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
     58 	waitSig(t, c1, syscall.SIGHUP)
     59 	t.Logf("sighup...")
     60 	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
     61 	waitSig(t, c1, syscall.SIGHUP)
     62 
     63 	// The first SIGHUP should be waiting for us on c.
     64 	waitSig(t, c, syscall.SIGHUP)
     65 }
     66 
     67 func TestStress(t *testing.T) {
     68 	dur := 3 * time.Second
     69 	if testing.Short() {
     70 		dur = 100 * time.Millisecond
     71 	}
     72 	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
     73 	done := make(chan bool)
     74 	finished := make(chan bool)
     75 	go func() {
     76 		sig := make(chan os.Signal, 1)
     77 		Notify(sig, syscall.SIGUSR1)
     78 		defer Stop(sig)
     79 	Loop:
     80 		for {
     81 			select {
     82 			case <-sig:
     83 			case <-done:
     84 				break Loop
     85 			}
     86 		}
     87 		finished <- true
     88 	}()
     89 	go func() {
     90 	Loop:
     91 		for {
     92 			select {
     93 			case <-done:
     94 				break Loop
     95 			default:
     96 				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
     97 				runtime.Gosched()
     98 			}
     99 		}
    100 		finished <- true
    101 	}()
    102 	time.Sleep(dur)
    103 	close(done)
    104 	<-finished
    105 	<-finished
    106 	// When run with 'go test -cpu=1,2,4' SIGUSR1 from this test can slip
    107 	// into subsequent TestSignal() causing failure.
    108 	// Sleep for a while to reduce the possibility of the failure.
    109 	time.Sleep(10 * time.Millisecond)
    110 }
    111 
    112 func testCancel(t *testing.T, ignore bool) {
    113 	// Send SIGWINCH. By default this signal should be ignored.
    114 	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
    115 	time.Sleep(100 * time.Millisecond)
    116 
    117 	// Ask to be notified on c1 when a SIGWINCH is received.
    118 	c1 := make(chan os.Signal, 1)
    119 	Notify(c1, syscall.SIGWINCH)
    120 	defer Stop(c1)
    121 
    122 	// Ask to be notified on c2 when a SIGHUP is received.
    123 	c2 := make(chan os.Signal, 1)
    124 	Notify(c2, syscall.SIGHUP)
    125 	defer Stop(c2)
    126 
    127 	// Send this process a SIGWINCH and wait for notification on c1.
    128 	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
    129 	waitSig(t, c1, syscall.SIGWINCH)
    130 
    131 	// Send this process a SIGHUP and wait for notification on c2.
    132 	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    133 	waitSig(t, c2, syscall.SIGHUP)
    134 
    135 	// Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
    136 	if ignore {
    137 		Ignore(syscall.SIGWINCH, syscall.SIGHUP)
    138 	} else {
    139 		Reset(syscall.SIGWINCH, syscall.SIGHUP)
    140 	}
    141 
    142 	// Send this process a SIGWINCH. It should be ignored.
    143 	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
    144 
    145 	// If ignoring, Send this process a SIGHUP. It should be ignored.
    146 	if ignore {
    147 		syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    148 	}
    149 
    150 	select {
    151 	case s := <-c1:
    152 		t.Fatalf("unexpected signal %v", s)
    153 	case <-time.After(100 * time.Millisecond):
    154 		// nothing to read - good
    155 	}
    156 
    157 	select {
    158 	case s := <-c2:
    159 		t.Fatalf("unexpected signal %v", s)
    160 	case <-time.After(100 * time.Millisecond):
    161 		// nothing to read - good
    162 	}
    163 
    164 	// Reset the signal handlers for all signals.
    165 	Reset()
    166 }
    167 
    168 // Test that Reset cancels registration for listed signals on all channels.
    169 func TestReset(t *testing.T) {
    170 	testCancel(t, false)
    171 }
    172 
    173 // Test that Ignore cancels registration for listed signals on all channels.
    174 func TestIgnore(t *testing.T) {
    175 	testCancel(t, true)
    176 }
    177 
    178 var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
    179 
    180 // Test that Stop cancels the channel's registrations.
    181 func TestStop(t *testing.T) {
    182 	sigs := []syscall.Signal{
    183 		syscall.SIGWINCH,
    184 		syscall.SIGHUP,
    185 	}
    186 
    187 	for _, sig := range sigs {
    188 		// Send the signal.
    189 		// If it's SIGWINCH, we should not see it.
    190 		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
    191 		if sig != syscall.SIGHUP || *sendUncaughtSighup == 1 {
    192 			syscall.Kill(syscall.Getpid(), sig)
    193 		}
    194 		time.Sleep(100 * time.Millisecond)
    195 
    196 		// Ask for signal
    197 		c := make(chan os.Signal, 1)
    198 		Notify(c, sig)
    199 		defer Stop(c)
    200 
    201 		// Send this process that signal
    202 		syscall.Kill(syscall.Getpid(), sig)
    203 		waitSig(t, c, sig)
    204 
    205 		Stop(c)
    206 		select {
    207 		case s := <-c:
    208 			t.Fatalf("unexpected signal %v", s)
    209 		case <-time.After(100 * time.Millisecond):
    210 			// nothing to read - good
    211 		}
    212 
    213 		// Send the signal.
    214 		// If it's SIGWINCH, we should not see it.
    215 		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
    216 		if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
    217 			syscall.Kill(syscall.Getpid(), sig)
    218 		}
    219 
    220 		select {
    221 		case s := <-c:
    222 			t.Fatalf("unexpected signal %v", s)
    223 		case <-time.After(100 * time.Millisecond):
    224 			// nothing to read - good
    225 		}
    226 	}
    227 }
    228 
    229 // Test that when run under nohup, an uncaught SIGHUP does not kill the program,
    230 // but a
    231 func TestNohup(t *testing.T) {
    232 	// Ugly: ask for SIGHUP so that child will not have no-hup set
    233 	// even if test is running under nohup environment.
    234 	// We have no intention of reading from c.
    235 	c := make(chan os.Signal, 1)
    236 	Notify(c, syscall.SIGHUP)
    237 
    238 	// When run without nohup, the test should crash on an uncaught SIGHUP.
    239 	// When run under nohup, the test should ignore uncaught SIGHUPs,
    240 	// because the runtime is not supposed to be listening for them.
    241 	// Either way, TestStop should still be able to catch them when it wants them
    242 	// and then when it stops wanting them, the original behavior should resume.
    243 	//
    244 	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
    245 	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
    246 	//
    247 	// Both should fail without nohup and succeed with nohup.
    248 
    249 	for i := 1; i <= 2; i++ {
    250 		out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
    251 		if err == nil {
    252 			t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
    253 		}
    254 	}
    255 
    256 	Stop(c)
    257 
    258 	// Again, this time with nohup, assuming we can find it.
    259 	_, err := os.Stat("/usr/bin/nohup")
    260 	if err != nil {
    261 		t.Skip("cannot find nohup; skipping second half of test")
    262 	}
    263 
    264 	for i := 1; i <= 2; i++ {
    265 		os.Remove("nohup.out")
    266 		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
    267 
    268 		data, _ := ioutil.ReadFile("nohup.out")
    269 		os.Remove("nohup.out")
    270 		if err != nil {
    271 			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)
    272 		}
    273 	}
    274 }
    275