github.com/zxy12/golang_with_comment@v0.0.0-20190701084843-0e6b2aff5ef3/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  	"bytes"
    11  	"flag"
    12  	"fmt"
    13  	"internal/testenv"
    14  	"io/ioutil"
    15  	"os"
    16  	"os/exec"
    17  	"runtime"
    18  	"strconv"
    19  	"sync"
    20  	"syscall"
    21  	"testing"
    22  	"time"
    23  )
    24  
    25  func 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  
    36  // Test that basic signal handling works.
    37  func TestSignal(t *testing.T) {
    38  	// Ask for SIGHUP
    39  	c := make(chan os.Signal, 1)
    40  	Notify(c, syscall.SIGHUP)
    41  	defer Stop(c)
    42  
    43  	// Send this process a SIGHUP
    44  	t.Logf("sighup...")
    45  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    46  	waitSig(t, c, syscall.SIGHUP)
    47  
    48  	// Ask for everything we can get.
    49  	c1 := make(chan os.Signal, 1)
    50  	Notify(c1)
    51  
    52  	// Send this process a SIGWINCH
    53  	t.Logf("sigwinch...")
    54  	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.
    60  	t.Logf("sighup...")
    61  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
    62  	waitSig(t, c1, syscall.SIGHUP)
    63  	t.Logf("sighup...")
    64  	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  }
    70  
    71  func 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)
    82  		defer Stop(sig)
    83  	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
   110  	// 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)
   114  }
   115  
   116  func 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  
   146  	// 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  
   159  	// 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.
   186  func TestReset(t *testing.T) {
   187  	testCancel(t, false)
   188  }
   189  
   190  // Test that Ignore cancels registration for listed signals on all channels.
   191  func TestIgnore(t *testing.T) {
   192  	testCancel(t, true)
   193  }
   194  
   195  var sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
   196  
   197  // Test that Stop cancels the channel's registrations.
   198  func TestStop(t *testing.T) {
   199  	sigs := []syscall.Signal{
   200  		syscall.SIGWINCH,
   201  		syscall.SIGHUP,
   202  		syscall.SIGUSR1,
   203  	}
   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.
   209  		if sig == syscall.SIGWINCH || (sig == syscall.SIGHUP && *sendUncaughtSighup == 1) {
   210  			syscall.Kill(syscall.Getpid(), sig)
   211  		}
   212  		time.Sleep(100 * time.Millisecond)
   213  
   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)
   227  		case <-time.After(100 * time.Millisecond):
   228  			// 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)
   241  		case <-time.After(100 * time.Millisecond):
   242  			// 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
   249  func 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  
   276  	// 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  
   282  	// 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  }
   299  
   300  // Test that SIGCONT works (issue 8953).
   301  func 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  }
   308  
   309  // Test race between stopping and receiving a signal (issue 14571).
   310  func TestAtomicStop(t *testing.T) {
   311  	if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
   312  		atomicStopTestProgram()
   313  		t.Fatal("atomicStopTestProgram returned")
   314  	}
   315  
   316  	testenv.MustHaveExec(t)
   317  
   318  	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 {
   324  			t.Logf("iteration %d: output %s", i, out)
   325  		} else {
   326  			t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
   327  		}
   328  
   329  		lost := bytes.Contains(out, []byte("lost signal"))
   330  		if lost {
   331  			t.Errorf("iteration %d: lost signal", i)
   332  		}
   333  
   334  		// The program should either die due to SIGINT,
   335  		// or exit with success without printing "lost signal".
   336  		if err == nil {
   337  			if len(out) > 0 && !lost {
   338  				t.Errorf("iteration %d: unexpected output", i)
   339  			}
   340  		} else {
   341  			if ee, ok := err.(*exec.ExitError); !ok {
   342  				t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err)
   343  			} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
   344  				t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys())
   345  			} else if !ws.Signaled() || ws.Signal() != syscall.SIGINT {
   346  				t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee)
   347  			}
   348  		}
   349  	}
   350  }
   351  
   352  // atomicStopTestProgram is run in a subprocess by TestAtomicStop.
   353  // It tries to trigger a signal delivery race. This function should
   354  // either catch a signal or die from it.
   355  func atomicStopTestProgram() {
   356  	const tries = 10
   357  	pid := syscall.Getpid()
   358  	printed := false
   359  	for i := 0; i < tries; i++ {
   360  		cs := make(chan os.Signal, 1)
   361  		Notify(cs, syscall.SIGINT)
   362  
   363  		var wg sync.WaitGroup
   364  		wg.Add(1)
   365  		go func() {
   366  			defer wg.Done()
   367  			Stop(cs)
   368  		}()
   369  
   370  		syscall.Kill(pid, syscall.SIGINT)
   371  
   372  		// At this point we should either die from SIGINT or
   373  		// get a notification on cs. If neither happens, we
   374  		// dropped the signal. Give it a second to deliver,
   375  		// which is far far longer than it should require.
   376  
   377  		select {
   378  		case <-cs:
   379  		case <-time.After(1 * time.Second):
   380  			if !printed {
   381  				fmt.Print("lost signal on iterations:")
   382  				printed = true
   383  			}
   384  			fmt.Printf(" %d", i)
   385  		}
   386  
   387  		wg.Wait()
   388  	}
   389  	if printed {
   390  		fmt.Print("\n")
   391  	}
   392  
   393  	os.Exit(0)
   394  }