github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/os/signal/signal_test.go (about)

     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  	// At this point we do not expect any further signals on c1.
   143  	// However, it is just barely possible that the initial SIGWINCH
   144  	// at the start of this function was delivered after we called
   145  	// Notify on c1. In that case the waitSig for SIGWINCH may have
   146  	// picked up that initial SIGWINCH, and the second SIGWINCH may
   147  	// then have been delivered on the channel. This sequence of events
   148  	// may have caused issue 15661.
   149  	// So, read any possible signal from the channel now.
   150  	select {
   151  	case <-c1:
   152  	default:
   153  	}
   154  
   155  	// Send this process a SIGWINCH. It should be ignored.
   156  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   157  
   158  	// If ignoring, Send this process a SIGHUP. It should be ignored.
   159  	if ignore {
   160  		syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   161  	}
   162  
   163  	select {
   164  	case s := <-c1:
   165  		t.Fatalf("unexpected signal %v", s)
   166  	case <-time.After(100 * time.Millisecond):
   167  		// nothing to read - good
   168  	}
   169  
   170  	select {
   171  	case s := <-c2:
   172  		t.Fatalf("unexpected signal %v", s)
   173  	case <-time.After(100 * time.Millisecond):
   174  		// nothing to read - good
   175  	}
   176  
   177  	// Reset the signal handlers for all signals.
   178  	Reset()
   179  }
   180  
   181  // Test that Reset cancels registration for listed signals on all channels.
   182  func TestReset(t *testing.T) {
   183  	testCancel(t, false)
   184  }
   185  
   186  // Test that Ignore cancels registration for listed signals on all channels.
   187  func TestIgnore(t *testing.T) {
   188  	testCancel(t, true)
   189  }
   190  
   191  var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
   192  
   193  // Test that Stop cancels the channel's registrations.
   194  func TestStop(t *testing.T) {
   195  	sigs := []syscall.Signal{
   196  		syscall.SIGWINCH,
   197  		syscall.SIGHUP,
   198  		syscall.SIGUSR1,
   199  	}
   200  
   201  	for _, sig := range sigs {
   202  		// Send the signal.
   203  		// If it's SIGWINCH, we should not see it.
   204  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   205  		if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
   206  			syscall.Kill(syscall.Getpid(), sig)
   207  		}
   208  		time.Sleep(100 * time.Millisecond)
   209  
   210  		// Ask for signal
   211  		c := make(chan os.Signal, 1)
   212  		Notify(c, sig)
   213  		defer Stop(c)
   214  
   215  		// Send this process that signal
   216  		syscall.Kill(syscall.Getpid(), sig)
   217  		waitSig(t, c, sig)
   218  
   219  		Stop(c)
   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  		// Send the signal.
   228  		// If it's SIGWINCH, we should not see it.
   229  		// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   230  		if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
   231  			syscall.Kill(syscall.Getpid(), sig)
   232  		}
   233  
   234  		select {
   235  		case s := <-c:
   236  			t.Fatalf("unexpected signal %v", s)
   237  		case <-time.After(100 * time.Millisecond):
   238  			// nothing to read - good
   239  		}
   240  	}
   241  }
   242  
   243  // Test that when run under nohup, an uncaught SIGHUP does not kill the program,
   244  // but a
   245  func TestNohup(t *testing.T) {
   246  	// Ugly: ask for SIGHUP so that child will not have no-hup set
   247  	// even if test is running under nohup environment.
   248  	// We have no intention of reading from c.
   249  	c := make(chan os.Signal, 1)
   250  	Notify(c, syscall.SIGHUP)
   251  
   252  	// When run without nohup, the test should crash on an uncaught SIGHUP.
   253  	// When run under nohup, the test should ignore uncaught SIGHUPs,
   254  	// because the runtime is not supposed to be listening for them.
   255  	// Either way, TestStop should still be able to catch them when it wants them
   256  	// and then when it stops wanting them, the original behavior should resume.
   257  	//
   258  	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
   259  	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
   260  	//
   261  	// Both should fail without nohup and succeed with nohup.
   262  
   263  	for i := 1; i <= 2; i++ {
   264  		out, err := exec.Command(os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   265  		if err == nil {
   266  			t.Fatalf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
   267  		}
   268  	}
   269  
   270  	Stop(c)
   271  
   272  	// Skip the nohup test below when running in tmux on darwin, since nohup
   273  	// doesn't work correctly there. See issue #5135.
   274  	if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
   275  		t.Skip("Skipping nohup test due to running in tmux on darwin")
   276  	}
   277  
   278  	// Again, this time with nohup, assuming we can find it.
   279  	_, err := os.Stat("/usr/bin/nohup")
   280  	if err != nil {
   281  		t.Skip("cannot find nohup; skipping second half of test")
   282  	}
   283  
   284  	for i := 1; i <= 2; i++ {
   285  		os.Remove("nohup.out")
   286  		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestStop", "-send_uncaught_sighup="+strconv.Itoa(i)).CombinedOutput()
   287  
   288  		data, _ := ioutil.ReadFile("nohup.out")
   289  		os.Remove("nohup.out")
   290  		if err != nil {
   291  			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)
   292  		}
   293  	}
   294  }
   295  
   296  // Test that SIGCONT works (issue 8953).
   297  func TestSIGCONT(t *testing.T) {
   298  	c := make(chan os.Signal, 1)
   299  	Notify(c, syscall.SIGCONT)
   300  	defer Stop(c)
   301  	syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
   302  	waitSig(t, c, syscall.SIGCONT)
   303  }