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

View as plain text