Source file src/os/signal/signal_test.go

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

View as plain text