Black Lives Matter. Support the Equal Justice Initiative.

Source file src/os/signal/signal_test.go

Documentation: os/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  //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
     6  // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
     7  
     8  package signal
     9  
    10  import (
    11  	"bytes"
    12  	"context"
    13  	"flag"
    14  	"fmt"
    15  	"internal/testenv"
    16  	"os"
    17  	"os/exec"
    18  	"runtime"
    19  	"runtime/trace"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"syscall"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  // settleTime is an upper bound on how long we expect signals to take to be
    29  // delivered. Lower values make the test faster, but also flakier — especially
    30  // on heavily loaded systems.
    31  //
    32  // The current value is set based on flakes observed in the Go builders.
    33  var settleTime = 100 * time.Millisecond
    34  
    35  // fatalWaitingTime is an absurdly long time to wait for signals to be
    36  // delivered but, using it, we (hopefully) eliminate test flakes on the
    37  // build servers. See #46736 for discussion.
    38  var fatalWaitingTime = 30 * time.Second
    39  
    40  func init() {
    41  	if testenv.Builder() == "solaris-amd64-oraclerel" {
    42  		// The solaris-amd64-oraclerel builder has been observed to time out in
    43  		// TestNohup even with a 250ms settle time.
    44  		//
    45  		// Use a much longer settle time on that builder to try to suss out whether
    46  		// the test is flaky due to builder slowness (which may mean we need a
    47  		// longer GO_TEST_TIMEOUT_SCALE) or due to a dropped signal (which may
    48  		// instead need a test-skip and upstream bug filed against the Solaris
    49  		// kernel).
    50  		//
    51  		// This constant is chosen so as to make the test as generous as possible
    52  		// while still reliably completing within 3 minutes in non-short mode.
    53  		//
    54  		// See https://golang.org/issue/33174.
    55  		settleTime = 11 * time.Second
    56  	} else if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "ppc64") {
    57  		// Older linux kernels seem to have some hiccups delivering the signal
    58  		// in a timely manner on ppc64 and ppc64le. When running on a
    59  		// ppc64le/ubuntu 16.04/linux 4.4 host the time can vary quite
    60  		// substantially even on a idle system. 5 seconds is twice any value
    61  		// observed when running 10000 tests on such a system.
    62  		settleTime = 5 * time.Second
    63  	} else if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
    64  		if scale, err := strconv.Atoi(s); err == nil {
    65  			settleTime *= time.Duration(scale)
    66  		}
    67  	}
    68  }
    69  
    70  func waitSig(t *testing.T, c <-chan os.Signal, sig os.Signal) {
    71  	t.Helper()
    72  	waitSig1(t, c, sig, false)
    73  }
    74  func waitSigAll(t *testing.T, c <-chan os.Signal, sig os.Signal) {
    75  	t.Helper()
    76  	waitSig1(t, c, sig, true)
    77  }
    78  
    79  func waitSig1(t *testing.T, c <-chan os.Signal, sig os.Signal, all bool) {
    80  	t.Helper()
    81  
    82  	// Sleep multiple times to give the kernel more tries to
    83  	// deliver the signal.
    84  	start := time.Now()
    85  	timer := time.NewTimer(settleTime / 10)
    86  	defer timer.Stop()
    87  	// If the caller notified for all signals on c, filter out SIGURG,
    88  	// which is used for runtime preemption and can come at unpredictable times.
    89  	// General user code should filter out all unexpected signals instead of just
    90  	// SIGURG, but since os/signal is tightly coupled to the runtime it seems
    91  	// appropriate to be stricter here.
    92  	for time.Since(start) < fatalWaitingTime {
    93  		select {
    94  		case s := <-c:
    95  			if s == sig {
    96  				return
    97  			}
    98  			if !all || s != syscall.SIGURG {
    99  				t.Fatalf("signal was %v, want %v", s, sig)
   100  			}
   101  		case <-timer.C:
   102  			timer.Reset(settleTime / 10)
   103  		}
   104  	}
   105  	t.Fatalf("timeout after %v waiting for %v", fatalWaitingTime, sig)
   106  }
   107  
   108  // quiesce waits until we can be reasonably confident that all pending signals
   109  // have been delivered by the OS.
   110  func quiesce() {
   111  	// The kernel will deliver a signal as a thread returns
   112  	// from a syscall. If the only active thread is sleeping,
   113  	// and the system is busy, the kernel may not get around
   114  	// to waking up a thread to catch the signal.
   115  	// We try splitting up the sleep to give the kernel
   116  	// many chances to deliver the signal.
   117  	start := time.Now()
   118  	for time.Since(start) < settleTime {
   119  		time.Sleep(settleTime / 10)
   120  	}
   121  }
   122  
   123  // Test that basic signal handling works.
   124  func TestSignal(t *testing.T) {
   125  	// Ask for SIGHUP
   126  	c := make(chan os.Signal, 1)
   127  	Notify(c, syscall.SIGHUP)
   128  	defer Stop(c)
   129  
   130  	// Send this process a SIGHUP
   131  	t.Logf("sighup...")
   132  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   133  	waitSig(t, c, syscall.SIGHUP)
   134  
   135  	// Ask for everything we can get. The buffer size has to be
   136  	// more than 1, since the runtime might send SIGURG signals.
   137  	// Using 10 is arbitrary.
   138  	c1 := make(chan os.Signal, 10)
   139  	Notify(c1)
   140  
   141  	// Send this process a SIGWINCH
   142  	t.Logf("sigwinch...")
   143  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   144  	waitSigAll(t, c1, syscall.SIGWINCH)
   145  
   146  	// Send two more SIGHUPs, to make sure that
   147  	// they get delivered on c1 and that not reading
   148  	// from c does not block everything.
   149  	t.Logf("sighup...")
   150  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   151  	waitSigAll(t, c1, syscall.SIGHUP)
   152  	t.Logf("sighup...")
   153  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   154  	waitSigAll(t, c1, syscall.SIGHUP)
   155  
   156  	// The first SIGHUP should be waiting for us on c.
   157  	waitSig(t, c, syscall.SIGHUP)
   158  }
   159  
   160  func TestStress(t *testing.T) {
   161  	dur := 3 * time.Second
   162  	if testing.Short() {
   163  		dur = 100 * time.Millisecond
   164  	}
   165  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   166  
   167  	sig := make(chan os.Signal, 1)
   168  	Notify(sig, syscall.SIGUSR1)
   169  
   170  	go func() {
   171  		stop := time.After(dur)
   172  		for {
   173  			select {
   174  			case <-stop:
   175  				// Allow enough time for all signals to be delivered before we stop
   176  				// listening for them.
   177  				quiesce()
   178  				Stop(sig)
   179  				// According to its documentation, “[w]hen Stop returns, it in
   180  				// guaranteed that c will receive no more signals.” So we can safely
   181  				// close sig here: if there is a send-after-close race here, that is a
   182  				// bug in Stop and we would like to detect it.
   183  				close(sig)
   184  				return
   185  
   186  			default:
   187  				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
   188  				runtime.Gosched()
   189  			}
   190  		}
   191  	}()
   192  
   193  	for range sig {
   194  		// Receive signals until the sender closes sig.
   195  	}
   196  }
   197  
   198  func testCancel(t *testing.T, ignore bool) {
   199  	// Ask to be notified on c1 when a SIGWINCH is received.
   200  	c1 := make(chan os.Signal, 1)
   201  	Notify(c1, syscall.SIGWINCH)
   202  	defer Stop(c1)
   203  
   204  	// Ask to be notified on c2 when a SIGHUP is received.
   205  	c2 := make(chan os.Signal, 1)
   206  	Notify(c2, syscall.SIGHUP)
   207  	defer Stop(c2)
   208  
   209  	// Send this process a SIGWINCH and wait for notification on c1.
   210  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   211  	waitSig(t, c1, syscall.SIGWINCH)
   212  
   213  	// Send this process a SIGHUP and wait for notification on c2.
   214  	syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   215  	waitSig(t, c2, syscall.SIGHUP)
   216  
   217  	// Ignore, or reset the signal handlers for, SIGWINCH and SIGHUP.
   218  	// Either way, this should undo both calls to Notify above.
   219  	if ignore {
   220  		Ignore(syscall.SIGWINCH, syscall.SIGHUP)
   221  		// Don't bother deferring a call to Reset: it is documented to undo Notify,
   222  		// but its documentation says nothing about Ignore, and (as of the time of
   223  		// writing) it empirically does not undo an Ignore.
   224  	} else {
   225  		Reset(syscall.SIGWINCH, syscall.SIGHUP)
   226  	}
   227  
   228  	// Send this process a SIGWINCH. It should be ignored.
   229  	syscall.Kill(syscall.Getpid(), syscall.SIGWINCH)
   230  
   231  	// If ignoring, Send this process a SIGHUP. It should be ignored.
   232  	if ignore {
   233  		syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
   234  	}
   235  
   236  	quiesce()
   237  
   238  	select {
   239  	case s := <-c1:
   240  		t.Errorf("unexpected signal %v", s)
   241  	default:
   242  		// nothing to read - good
   243  	}
   244  
   245  	select {
   246  	case s := <-c2:
   247  		t.Errorf("unexpected signal %v", s)
   248  	default:
   249  		// nothing to read - good
   250  	}
   251  
   252  	// One or both of the signals may have been blocked for this process
   253  	// by the calling process.
   254  	// Discard any queued signals now to avoid interfering with other tests.
   255  	Notify(c1, syscall.SIGWINCH)
   256  	Notify(c2, syscall.SIGHUP)
   257  	quiesce()
   258  }
   259  
   260  // Test that Reset cancels registration for listed signals on all channels.
   261  func TestReset(t *testing.T) {
   262  	testCancel(t, false)
   263  }
   264  
   265  // Test that Ignore cancels registration for listed signals on all channels.
   266  func TestIgnore(t *testing.T) {
   267  	testCancel(t, true)
   268  }
   269  
   270  // Test that Ignored correctly detects changes to the ignored status of a signal.
   271  func TestIgnored(t *testing.T) {
   272  	// Ask to be notified on SIGWINCH.
   273  	c := make(chan os.Signal, 1)
   274  	Notify(c, syscall.SIGWINCH)
   275  
   276  	// If we're being notified, then the signal should not be ignored.
   277  	if Ignored(syscall.SIGWINCH) {
   278  		t.Errorf("expected SIGWINCH to not be ignored.")
   279  	}
   280  	Stop(c)
   281  	Ignore(syscall.SIGWINCH)
   282  
   283  	// We're no longer paying attention to this signal.
   284  	if !Ignored(syscall.SIGWINCH) {
   285  		t.Errorf("expected SIGWINCH to be ignored when explicitly ignoring it.")
   286  	}
   287  
   288  	Reset()
   289  }
   290  
   291  var checkSighupIgnored = flag.Bool("check_sighup_ignored", false, "if true, TestDetectNohup will fail if SIGHUP is not ignored.")
   292  
   293  // Test that Ignored(SIGHUP) correctly detects whether it is being run under nohup.
   294  func TestDetectNohup(t *testing.T) {
   295  	if *checkSighupIgnored {
   296  		if !Ignored(syscall.SIGHUP) {
   297  			t.Fatal("SIGHUP is not ignored.")
   298  		} else {
   299  			t.Log("SIGHUP is ignored.")
   300  		}
   301  	} else {
   302  		defer Reset()
   303  		// Ugly: ask for SIGHUP so that child will not have no-hup set
   304  		// even if test is running under nohup environment.
   305  		// We have no intention of reading from c.
   306  		c := make(chan os.Signal, 1)
   307  		Notify(c, syscall.SIGHUP)
   308  		if out, err := exec.Command(os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput(); err == nil {
   309  			t.Errorf("ran test with -check_sighup_ignored and it succeeded: expected failure.\nOutput:\n%s", out)
   310  		}
   311  		Stop(c)
   312  		// Again, this time with nohup, assuming we can find it.
   313  		_, err := os.Stat("/usr/bin/nohup")
   314  		if err != nil {
   315  			t.Skip("cannot find nohup; skipping second half of test")
   316  		}
   317  		Ignore(syscall.SIGHUP)
   318  		os.Remove("nohup.out")
   319  		out, err := exec.Command("/usr/bin/nohup", os.Args[0], "-test.run=TestDetectNohup", "-check_sighup_ignored").CombinedOutput()
   320  
   321  		data, _ := os.ReadFile("nohup.out")
   322  		os.Remove("nohup.out")
   323  		if err != nil {
   324  			t.Errorf("ran test with -check_sighup_ignored under nohup and it failed: expected success.\nError: %v\nOutput:\n%s%s", err, out, data)
   325  		}
   326  	}
   327  }
   328  
   329  var (
   330  	sendUncaughtSighup = flag.Int("send_uncaught_sighup", 0, "send uncaught SIGHUP during TestStop")
   331  	dieFromSighup      = flag.Bool("die_from_sighup", false, "wait to die from uncaught SIGHUP")
   332  )
   333  
   334  // Test that Stop cancels the channel's registrations.
   335  func TestStop(t *testing.T) {
   336  	sigs := []syscall.Signal{
   337  		syscall.SIGWINCH,
   338  		syscall.SIGHUP,
   339  		syscall.SIGUSR1,
   340  	}
   341  
   342  	for _, sig := range sigs {
   343  		sig := sig
   344  		t.Run(fmt.Sprint(sig), func(t *testing.T) {
   345  			// When calling Notify with a specific signal,
   346  			// independent signals should not interfere with each other,
   347  			// and we end up needing to wait for signals to quiesce a lot.
   348  			// Test the three different signals concurrently.
   349  			t.Parallel()
   350  
   351  			// If the signal is not ignored, send the signal before registering a
   352  			// channel to verify the behavior of the default Go handler.
   353  			// If it's SIGWINCH or SIGUSR1 we should not see it.
   354  			// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   355  			mayHaveBlockedSignal := false
   356  			if !Ignored(sig) && (sig != syscall.SIGHUP || *sendUncaughtSighup == 1) {
   357  				syscall.Kill(syscall.Getpid(), sig)
   358  				quiesce()
   359  
   360  				// We don't know whether sig is blocked for this process; see
   361  				// https://golang.org/issue/38165. Assume that it could be.
   362  				mayHaveBlockedSignal = true
   363  			}
   364  
   365  			// Ask for signal
   366  			c := make(chan os.Signal, 1)
   367  			Notify(c, sig)
   368  
   369  			// Send this process the signal again.
   370  			syscall.Kill(syscall.Getpid(), sig)
   371  			waitSig(t, c, sig)
   372  
   373  			if mayHaveBlockedSignal {
   374  				// We may have received a queued initial signal in addition to the one
   375  				// that we sent after Notify. If so, waitSig may have observed that
   376  				// initial signal instead of the second one, and we may need to wait for
   377  				// the second signal to clear. Do that now.
   378  				quiesce()
   379  				select {
   380  				case <-c:
   381  				default:
   382  				}
   383  			}
   384  
   385  			// Stop watching for the signal and send it again.
   386  			// If it's SIGHUP, maybe we'll die. Let the flag tell us what to do.
   387  			Stop(c)
   388  			if sig != syscall.SIGHUP || *sendUncaughtSighup == 2 {
   389  				syscall.Kill(syscall.Getpid(), sig)
   390  				quiesce()
   391  
   392  				select {
   393  				case s := <-c:
   394  					t.Errorf("unexpected signal %v", s)
   395  				default:
   396  					// nothing to read - good
   397  				}
   398  
   399  				// If we're going to receive a signal, it has almost certainly been
   400  				// received by now. However, it may have been blocked for this process —
   401  				// we don't know. Explicitly unblock it and wait for it to clear now.
   402  				Notify(c, sig)
   403  				quiesce()
   404  				Stop(c)
   405  			}
   406  		})
   407  	}
   408  }
   409  
   410  // Test that when run under nohup, an uncaught SIGHUP does not kill the program.
   411  func TestNohup(t *testing.T) {
   412  	// Ugly: ask for SIGHUP so that child will not have no-hup set
   413  	// even if test is running under nohup environment.
   414  	// We have no intention of reading from c.
   415  	c := make(chan os.Signal, 1)
   416  	Notify(c, syscall.SIGHUP)
   417  
   418  	// When run without nohup, the test should crash on an uncaught SIGHUP.
   419  	// When run under nohup, the test should ignore uncaught SIGHUPs,
   420  	// because the runtime is not supposed to be listening for them.
   421  	// Either way, TestStop should still be able to catch them when it wants them
   422  	// and then when it stops wanting them, the original behavior should resume.
   423  	//
   424  	// send_uncaught_sighup=1 sends the SIGHUP before starting to listen for SIGHUPs.
   425  	// send_uncaught_sighup=2 sends the SIGHUP after no longer listening for SIGHUPs.
   426  	//
   427  	// Both should fail without nohup and succeed with nohup.
   428  
   429  	var subTimeout time.Duration
   430  
   431  	var wg sync.WaitGroup
   432  	wg.Add(2)
   433  	if deadline, ok := t.Deadline(); ok {
   434  		subTimeout = time.Until(deadline)
   435  		subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output.
   436  	}
   437  	for i := 1; i <= 2; i++ {
   438  		i := i
   439  		go t.Run(fmt.Sprintf("uncaught-%d", i), func(t *testing.T) {
   440  			defer wg.Done()
   441  
   442  			args := []string{
   443  				"-test.v",
   444  				"-test.run=TestStop",
   445  				"-send_uncaught_sighup=" + strconv.Itoa(i),
   446  				"-die_from_sighup",
   447  			}
   448  			if subTimeout != 0 {
   449  				args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
   450  			}
   451  			out, err := exec.Command(os.Args[0], args...).CombinedOutput()
   452  
   453  			if err == nil {
   454  				t.Errorf("ran test with -send_uncaught_sighup=%d and it succeeded: expected failure.\nOutput:\n%s", i, out)
   455  			} else {
   456  				t.Logf("test with -send_uncaught_sighup=%d failed as expected.\nError: %v\nOutput:\n%s", i, err, out)
   457  			}
   458  		})
   459  	}
   460  	wg.Wait()
   461  
   462  	Stop(c)
   463  
   464  	// Skip the nohup test below when running in tmux on darwin, since nohup
   465  	// doesn't work correctly there. See issue #5135.
   466  	if runtime.GOOS == "darwin" && os.Getenv("TMUX") != "" {
   467  		t.Skip("Skipping nohup test due to running in tmux on darwin")
   468  	}
   469  
   470  	// Again, this time with nohup, assuming we can find it.
   471  	_, err := exec.LookPath("nohup")
   472  	if err != nil {
   473  		t.Skip("cannot find nohup; skipping second half of test")
   474  	}
   475  
   476  	wg.Add(2)
   477  	if deadline, ok := t.Deadline(); ok {
   478  		subTimeout = time.Until(deadline)
   479  		subTimeout -= subTimeout / 10 // Leave 10% headroom for propagating output.
   480  	}
   481  	for i := 1; i <= 2; i++ {
   482  		i := i
   483  		go t.Run(fmt.Sprintf("nohup-%d", i), func(t *testing.T) {
   484  			defer wg.Done()
   485  
   486  			// POSIX specifies that nohup writes to a file named nohup.out if standard
   487  			// output is a terminal. However, for an exec.Command, standard output is
   488  			// not a terminal — so we don't need to read or remove that file (and,
   489  			// indeed, cannot even create it if the current user is unable to write to
   490  			// GOROOT/src, such as when GOROOT is installed and owned by root).
   491  
   492  			args := []string{
   493  				os.Args[0],
   494  				"-test.v",
   495  				"-test.run=TestStop",
   496  				"-send_uncaught_sighup=" + strconv.Itoa(i),
   497  			}
   498  			if subTimeout != 0 {
   499  				args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
   500  			}
   501  			out, err := exec.Command("nohup", args...).CombinedOutput()
   502  
   503  			if err != nil {
   504  				t.Errorf("ran test with -send_uncaught_sighup=%d under nohup and it failed: expected success.\nError: %v\nOutput:\n%s", i, err, out)
   505  			} else {
   506  				t.Logf("ran test with -send_uncaught_sighup=%d under nohup.\nOutput:\n%s", i, out)
   507  			}
   508  		})
   509  	}
   510  	wg.Wait()
   511  }
   512  
   513  // Test that SIGCONT works (issue 8953).
   514  func TestSIGCONT(t *testing.T) {
   515  	c := make(chan os.Signal, 1)
   516  	Notify(c, syscall.SIGCONT)
   517  	defer Stop(c)
   518  	syscall.Kill(syscall.Getpid(), syscall.SIGCONT)
   519  	waitSig(t, c, syscall.SIGCONT)
   520  }
   521  
   522  // Test race between stopping and receiving a signal (issue 14571).
   523  func TestAtomicStop(t *testing.T) {
   524  	if os.Getenv("GO_TEST_ATOMIC_STOP") != "" {
   525  		atomicStopTestProgram(t)
   526  		t.Fatal("atomicStopTestProgram returned")
   527  	}
   528  
   529  	testenv.MustHaveExec(t)
   530  
   531  	// Call Notify for SIGINT before starting the child process.
   532  	// That ensures that SIGINT is not ignored for the child.
   533  	// This is necessary because if SIGINT is ignored when a
   534  	// Go program starts, then it remains ignored, and closing
   535  	// the last notification channel for SIGINT will switch it
   536  	// back to being ignored. In that case the assumption of
   537  	// atomicStopTestProgram, that it will either die from SIGINT
   538  	// or have it be reported, breaks down, as there is a third
   539  	// option: SIGINT might be ignored.
   540  	cs := make(chan os.Signal, 1)
   541  	Notify(cs, syscall.SIGINT)
   542  	defer Stop(cs)
   543  
   544  	const execs = 10
   545  	for i := 0; i < execs; i++ {
   546  		timeout := "0"
   547  		if deadline, ok := t.Deadline(); ok {
   548  			timeout = time.Until(deadline).String()
   549  		}
   550  		cmd := exec.Command(os.Args[0], "-test.run=TestAtomicStop", "-test.timeout="+timeout)
   551  		cmd.Env = append(os.Environ(), "GO_TEST_ATOMIC_STOP=1")
   552  		out, err := cmd.CombinedOutput()
   553  		if err == nil {
   554  			if len(out) > 0 {
   555  				t.Logf("iteration %d: output %s", i, out)
   556  			}
   557  		} else {
   558  			t.Logf("iteration %d: exit status %q: output: %s", i, err, out)
   559  		}
   560  
   561  		lost := bytes.Contains(out, []byte("lost signal"))
   562  		if lost {
   563  			t.Errorf("iteration %d: lost signal", i)
   564  		}
   565  
   566  		// The program should either die due to SIGINT,
   567  		// or exit with success without printing "lost signal".
   568  		if err == nil {
   569  			if len(out) > 0 && !lost {
   570  				t.Errorf("iteration %d: unexpected output", i)
   571  			}
   572  		} else {
   573  			if ee, ok := err.(*exec.ExitError); !ok {
   574  				t.Errorf("iteration %d: error (%v) has type %T; expected exec.ExitError", i, err, err)
   575  			} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
   576  				t.Errorf("iteration %d: error.Sys (%v) has type %T; expected syscall.WaitStatus", i, ee.Sys(), ee.Sys())
   577  			} else if !ws.Signaled() || ws.Signal() != syscall.SIGINT {
   578  				t.Errorf("iteration %d: got exit status %v; expected SIGINT", i, ee)
   579  			}
   580  		}
   581  	}
   582  }
   583  
   584  // atomicStopTestProgram is run in a subprocess by TestAtomicStop.
   585  // It tries to trigger a signal delivery race. This function should
   586  // either catch a signal or die from it.
   587  func atomicStopTestProgram(t *testing.T) {
   588  	// This test won't work if SIGINT is ignored here.
   589  	if Ignored(syscall.SIGINT) {
   590  		fmt.Println("SIGINT is ignored")
   591  		os.Exit(1)
   592  	}
   593  
   594  	const tries = 10
   595  
   596  	timeout := 2 * time.Second
   597  	if deadline, ok := t.Deadline(); ok {
   598  		// Give each try an equal slice of the deadline, with one slice to spare for
   599  		// cleanup.
   600  		timeout = time.Until(deadline) / (tries + 1)
   601  	}
   602  
   603  	pid := syscall.Getpid()
   604  	printed := false
   605  	for i := 0; i < tries; i++ {
   606  		cs := make(chan os.Signal, 1)
   607  		Notify(cs, syscall.SIGINT)
   608  
   609  		var wg sync.WaitGroup
   610  		wg.Add(1)
   611  		go func() {
   612  			defer wg.Done()
   613  			Stop(cs)
   614  		}()
   615  
   616  		syscall.Kill(pid, syscall.SIGINT)
   617  
   618  		// At this point we should either die from SIGINT or
   619  		// get a notification on cs. If neither happens, we
   620  		// dropped the signal. It is given 2 seconds to
   621  		// deliver, as needed for gccgo on some loaded test systems.
   622  
   623  		select {
   624  		case <-cs:
   625  		case <-time.After(timeout):
   626  			if !printed {
   627  				fmt.Print("lost signal on tries:")
   628  				printed = true
   629  			}
   630  			fmt.Printf(" %d", i)
   631  		}
   632  
   633  		wg.Wait()
   634  	}
   635  	if printed {
   636  		fmt.Print("\n")
   637  	}
   638  
   639  	os.Exit(0)
   640  }
   641  
   642  func TestTime(t *testing.T) {
   643  	// Test that signal works fine when we are in a call to get time,
   644  	// which on some platforms is using VDSO. See issue #34391.
   645  	dur := 3 * time.Second
   646  	if testing.Short() {
   647  		dur = 100 * time.Millisecond
   648  	}
   649  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   650  
   651  	sig := make(chan os.Signal, 1)
   652  	Notify(sig, syscall.SIGUSR1)
   653  
   654  	stop := make(chan struct{})
   655  	go func() {
   656  		for {
   657  			select {
   658  			case <-stop:
   659  				// Allow enough time for all signals to be delivered before we stop
   660  				// listening for them.
   661  				quiesce()
   662  				Stop(sig)
   663  				// According to its documentation, “[w]hen Stop returns, it in
   664  				// guaranteed that c will receive no more signals.” So we can safely
   665  				// close sig here: if there is a send-after-close race, that is a bug in
   666  				// Stop and we would like to detect it.
   667  				close(sig)
   668  				return
   669  
   670  			default:
   671  				syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
   672  				runtime.Gosched()
   673  			}
   674  		}
   675  	}()
   676  
   677  	done := make(chan struct{})
   678  	go func() {
   679  		for range sig {
   680  			// Receive signals until the sender closes sig.
   681  		}
   682  		close(done)
   683  	}()
   684  
   685  	t0 := time.Now()
   686  	for t1 := t0; t1.Sub(t0) < dur; t1 = time.Now() {
   687  	} // hammering on getting time
   688  
   689  	close(stop)
   690  	<-done
   691  }
   692  
   693  var (
   694  	checkNotifyContext = flag.Bool("check_notify_ctx", false, "if true, TestNotifyContext will fail if SIGINT is not received.")
   695  	ctxNotifyTimes     = flag.Int("ctx_notify_times", 1, "number of times a SIGINT signal should be received")
   696  )
   697  
   698  func TestNotifyContextNotifications(t *testing.T) {
   699  	if *checkNotifyContext {
   700  		ctx, _ := NotifyContext(context.Background(), syscall.SIGINT)
   701  		// We want to make sure not to be calling Stop() internally on NotifyContext() when processing a received signal.
   702  		// Being able to wait for a number of received system signals allows us to do so.
   703  		var wg sync.WaitGroup
   704  		n := *ctxNotifyTimes
   705  		wg.Add(n)
   706  		for i := 0; i < n; i++ {
   707  			go func() {
   708  				syscall.Kill(syscall.Getpid(), syscall.SIGINT)
   709  				wg.Done()
   710  			}()
   711  		}
   712  		wg.Wait()
   713  		<-ctx.Done()
   714  		fmt.Print("received SIGINT")
   715  		// Sleep to give time to simultaneous signals to reach the process.
   716  		// These signals must be ignored given stop() is not called on this code.
   717  		// We want to guarantee a SIGINT doesn't cause a premature termination of the program.
   718  		time.Sleep(settleTime)
   719  		return
   720  	}
   721  
   722  	t.Parallel()
   723  	testCases := []struct {
   724  		name string
   725  		n    int // number of times a SIGINT should be notified.
   726  	}{
   727  		{"once", 1},
   728  		{"multiple", 10},
   729  	}
   730  	for _, tc := range testCases {
   731  		t.Run(tc.name, func(t *testing.T) {
   732  			var subTimeout time.Duration
   733  			if deadline, ok := t.Deadline(); ok {
   734  				subTimeout := time.Until(deadline)
   735  				subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess.
   736  			}
   737  
   738  			args := []string{
   739  				"-test.v",
   740  				"-test.run=TestNotifyContextNotifications$",
   741  				"-check_notify_ctx",
   742  				fmt.Sprintf("-ctx_notify_times=%d", tc.n),
   743  			}
   744  			if subTimeout != 0 {
   745  				args = append(args, fmt.Sprintf("-test.timeout=%v", subTimeout))
   746  			}
   747  			out, err := exec.Command(os.Args[0], args...).CombinedOutput()
   748  			if err != nil {
   749  				t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
   750  			}
   751  			if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
   752  				t.Errorf("got %q, wanted %q", out, want)
   753  			}
   754  		})
   755  	}
   756  }
   757  
   758  func TestNotifyContextStop(t *testing.T) {
   759  	Ignore(syscall.SIGHUP)
   760  	if !Ignored(syscall.SIGHUP) {
   761  		t.Errorf("expected SIGHUP to be ignored when explicitly ignoring it.")
   762  	}
   763  
   764  	parent, cancelParent := context.WithCancel(context.Background())
   765  	defer cancelParent()
   766  	c, stop := NotifyContext(parent, syscall.SIGHUP)
   767  	defer stop()
   768  
   769  	// If we're being notified, then the signal should not be ignored.
   770  	if Ignored(syscall.SIGHUP) {
   771  		t.Errorf("expected SIGHUP to not be ignored.")
   772  	}
   773  
   774  	if want, got := "signal.NotifyContext(context.Background.WithCancel, [hangup])", fmt.Sprint(c); want != got {
   775  		t.Errorf("c.String() = %q, wanted %q", got, want)
   776  	}
   777  
   778  	stop()
   779  	select {
   780  	case <-c.Done():
   781  		if got := c.Err(); got != context.Canceled {
   782  			t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
   783  		}
   784  	case <-time.After(time.Second):
   785  		t.Errorf("timed out waiting for context to be done after calling stop")
   786  	}
   787  }
   788  
   789  func TestNotifyContextCancelParent(t *testing.T) {
   790  	parent, cancelParent := context.WithCancel(context.Background())
   791  	defer cancelParent()
   792  	c, stop := NotifyContext(parent, syscall.SIGINT)
   793  	defer stop()
   794  
   795  	if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
   796  		t.Errorf("c.String() = %q, want %q", got, want)
   797  	}
   798  
   799  	cancelParent()
   800  	select {
   801  	case <-c.Done():
   802  		if got := c.Err(); got != context.Canceled {
   803  			t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
   804  		}
   805  	case <-time.After(time.Second):
   806  		t.Errorf("timed out waiting for parent context to be canceled")
   807  	}
   808  }
   809  
   810  func TestNotifyContextPrematureCancelParent(t *testing.T) {
   811  	parent, cancelParent := context.WithCancel(context.Background())
   812  	defer cancelParent()
   813  
   814  	cancelParent() // Prematurely cancel context before calling NotifyContext.
   815  	c, stop := NotifyContext(parent, syscall.SIGINT)
   816  	defer stop()
   817  
   818  	if want, got := "signal.NotifyContext(context.Background.WithCancel, [interrupt])", fmt.Sprint(c); want != got {
   819  		t.Errorf("c.String() = %q, want %q", got, want)
   820  	}
   821  
   822  	select {
   823  	case <-c.Done():
   824  		if got := c.Err(); got != context.Canceled {
   825  			t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
   826  		}
   827  	case <-time.After(time.Second):
   828  		t.Errorf("timed out waiting for parent context to be canceled")
   829  	}
   830  }
   831  
   832  func TestNotifyContextSimultaneousStop(t *testing.T) {
   833  	c, stop := NotifyContext(context.Background(), syscall.SIGINT)
   834  	defer stop()
   835  
   836  	if want, got := "signal.NotifyContext(context.Background, [interrupt])", fmt.Sprint(c); want != got {
   837  		t.Errorf("c.String() = %q, want %q", got, want)
   838  	}
   839  
   840  	var wg sync.WaitGroup
   841  	n := 10
   842  	wg.Add(n)
   843  	for i := 0; i < n; i++ {
   844  		go func() {
   845  			stop()
   846  			wg.Done()
   847  		}()
   848  	}
   849  	wg.Wait()
   850  	select {
   851  	case <-c.Done():
   852  		if got := c.Err(); got != context.Canceled {
   853  			t.Errorf("c.Err() = %q, want %q", got, context.Canceled)
   854  		}
   855  	case <-time.After(time.Second):
   856  		t.Errorf("expected context to be canceled")
   857  	}
   858  }
   859  
   860  func TestNotifyContextStringer(t *testing.T) {
   861  	parent, cancelParent := context.WithCancel(context.Background())
   862  	defer cancelParent()
   863  	c, stop := NotifyContext(parent, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
   864  	defer stop()
   865  
   866  	want := `signal.NotifyContext(context.Background.WithCancel, [hangup interrupt terminated])`
   867  	if got := fmt.Sprint(c); got != want {
   868  		t.Errorf("c.String() = %q, want %q", got, want)
   869  	}
   870  }
   871  
   872  // #44193 test signal handling while stopping and starting the world.
   873  func TestSignalTrace(t *testing.T) {
   874  	done := make(chan struct{})
   875  	quit := make(chan struct{})
   876  	c := make(chan os.Signal, 1)
   877  	Notify(c, syscall.SIGHUP)
   878  
   879  	// Source and sink for signals busy loop unsynchronized with
   880  	// trace starts and stops. We are ultimately validating that
   881  	// signals and runtime.(stop|start)TheWorldGC are compatible.
   882  	go func() {
   883  		defer close(done)
   884  		defer Stop(c)
   885  		pid := syscall.Getpid()
   886  		for {
   887  			select {
   888  			case <-quit:
   889  				return
   890  			default:
   891  				syscall.Kill(pid, syscall.SIGHUP)
   892  			}
   893  			waitSig(t, c, syscall.SIGHUP)
   894  		}
   895  	}()
   896  
   897  	for i := 0; i < 100; i++ {
   898  		buf := new(bytes.Buffer)
   899  		if err := trace.Start(buf); err != nil {
   900  			t.Fatalf("[%d] failed to start tracing: %v", i, err)
   901  		}
   902  		time.After(1 * time.Microsecond)
   903  		trace.Stop()
   904  		size := buf.Len()
   905  		if size == 0 {
   906  			t.Fatalf("[%d] trace is empty", i)
   907  		}
   908  	}
   909  	close(quit)
   910  	<-done
   911  }
   912  

View as plain text