Source file src/runtime/testdata/testprogcgo/eintr.go

     1  // Copyright 2020 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 !plan9 && !windows
     6  // +build !plan9,!windows
     7  
     8  package main
     9  
    10  /*
    11  #include <errno.h>
    12  #include <signal.h>
    13  #include <string.h>
    14  
    15  static int clearRestart(int sig) {
    16  	struct sigaction sa;
    17  
    18  	memset(&sa, 0, sizeof sa);
    19  	if (sigaction(sig, NULL, &sa) < 0) {
    20  		return errno;
    21  	}
    22  	sa.sa_flags &=~ SA_RESTART;
    23  	if (sigaction(sig, &sa, NULL) < 0) {
    24  		return errno;
    25  	}
    26  	return 0;
    27  }
    28  */
    29  import "C"
    30  
    31  import (
    32  	"bytes"
    33  	"errors"
    34  	"fmt"
    35  	"io"
    36  	"log"
    37  	"net"
    38  	"os"
    39  	"os/exec"
    40  	"sync"
    41  	"syscall"
    42  	"time"
    43  )
    44  
    45  func init() {
    46  	register("EINTR", EINTR)
    47  	register("Block", Block)
    48  }
    49  
    50  // Test various operations when a signal handler is installed without
    51  // the SA_RESTART flag. This tests that the os and net APIs handle EINTR.
    52  func EINTR() {
    53  	if errno := C.clearRestart(C.int(syscall.SIGURG)); errno != 0 {
    54  		log.Fatal(syscall.Errno(errno))
    55  	}
    56  	if errno := C.clearRestart(C.int(syscall.SIGWINCH)); errno != 0 {
    57  		log.Fatal(syscall.Errno(errno))
    58  	}
    59  	if errno := C.clearRestart(C.int(syscall.SIGCHLD)); errno != 0 {
    60  		log.Fatal(syscall.Errno(errno))
    61  	}
    62  
    63  	var wg sync.WaitGroup
    64  	testPipe(&wg)
    65  	testNet(&wg)
    66  	testExec(&wg)
    67  	wg.Wait()
    68  	fmt.Println("OK")
    69  }
    70  
    71  // spin does CPU bound spinning and allocating for a millisecond,
    72  // to get a SIGURG.
    73  //
    74  //go:noinline
    75  func spin() (float64, []byte) {
    76  	stop := time.Now().Add(time.Millisecond)
    77  	r1 := 0.0
    78  	r2 := make([]byte, 200)
    79  	for time.Now().Before(stop) {
    80  		for i := 1; i < 1e6; i++ {
    81  			r1 += r1 / float64(i)
    82  			r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)...)
    83  			r2 = r2[100:]
    84  		}
    85  	}
    86  	return r1, r2
    87  }
    88  
    89  // winch sends a few SIGWINCH signals to the process.
    90  func winch() {
    91  	ticker := time.NewTicker(100 * time.Microsecond)
    92  	defer ticker.Stop()
    93  	pid := syscall.Getpid()
    94  	for n := 10; n > 0; n-- {
    95  		syscall.Kill(pid, syscall.SIGWINCH)
    96  		<-ticker.C
    97  	}
    98  }
    99  
   100  // sendSomeSignals triggers a few SIGURG and SIGWINCH signals.
   101  func sendSomeSignals() {
   102  	done := make(chan struct{})
   103  	go func() {
   104  		spin()
   105  		close(done)
   106  	}()
   107  	winch()
   108  	<-done
   109  }
   110  
   111  // testPipe tests pipe operations.
   112  func testPipe(wg *sync.WaitGroup) {
   113  	r, w, err := os.Pipe()
   114  	if err != nil {
   115  		log.Fatal(err)
   116  	}
   117  	if err := syscall.SetNonblock(int(r.Fd()), false); err != nil {
   118  		log.Fatal(err)
   119  	}
   120  	if err := syscall.SetNonblock(int(w.Fd()), false); err != nil {
   121  		log.Fatal(err)
   122  	}
   123  	wg.Add(2)
   124  	go func() {
   125  		defer wg.Done()
   126  		defer w.Close()
   127  		// Spin before calling Write so that the first ReadFull
   128  		// in the other goroutine will likely be interrupted
   129  		// by a signal.
   130  		sendSomeSignals()
   131  		// This Write will likely be interrupted by a signal
   132  		// as the other goroutine spins in the middle of reading.
   133  		// We write enough data that we should always fill the
   134  		// pipe buffer and need multiple write system calls.
   135  		if _, err := w.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil {
   136  			log.Fatal(err)
   137  		}
   138  	}()
   139  	go func() {
   140  		defer wg.Done()
   141  		defer r.Close()
   142  		b := make([]byte, 1<<20)
   143  		// This ReadFull will likely be interrupted by a signal,
   144  		// as the other goroutine spins before writing anything.
   145  		if _, err := io.ReadFull(r, b); err != nil {
   146  			log.Fatal(err)
   147  		}
   148  		// Spin after reading half the data so that the Write
   149  		// in the other goroutine will likely be interrupted
   150  		// before it completes.
   151  		sendSomeSignals()
   152  		if _, err := io.ReadFull(r, b); err != nil {
   153  			log.Fatal(err)
   154  		}
   155  	}()
   156  }
   157  
   158  // testNet tests network operations.
   159  func testNet(wg *sync.WaitGroup) {
   160  	ln, err := net.Listen("tcp4", "127.0.0.1:0")
   161  	if err != nil {
   162  		if errors.Is(err, syscall.EAFNOSUPPORT) || errors.Is(err, syscall.EPROTONOSUPPORT) {
   163  			return
   164  		}
   165  		log.Fatal(err)
   166  	}
   167  	wg.Add(2)
   168  	go func() {
   169  		defer wg.Done()
   170  		defer ln.Close()
   171  		c, err := ln.Accept()
   172  		if err != nil {
   173  			log.Fatal(err)
   174  		}
   175  		defer c.Close()
   176  		cf, err := c.(*net.TCPConn).File()
   177  		if err != nil {
   178  			log.Fatal(err)
   179  		}
   180  		defer cf.Close()
   181  		if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil {
   182  			log.Fatal(err)
   183  		}
   184  		// See comments in testPipe.
   185  		sendSomeSignals()
   186  		if _, err := cf.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil {
   187  			log.Fatal(err)
   188  		}
   189  	}()
   190  	go func() {
   191  		defer wg.Done()
   192  		sendSomeSignals()
   193  		c, err := net.Dial("tcp", ln.Addr().String())
   194  		if err != nil {
   195  			log.Fatal(err)
   196  		}
   197  		defer c.Close()
   198  		cf, err := c.(*net.TCPConn).File()
   199  		if err != nil {
   200  			log.Fatal(err)
   201  		}
   202  		defer cf.Close()
   203  		if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil {
   204  			log.Fatal(err)
   205  		}
   206  		// See comments in testPipe.
   207  		b := make([]byte, 1<<20)
   208  		if _, err := io.ReadFull(cf, b); err != nil {
   209  			log.Fatal(err)
   210  		}
   211  		sendSomeSignals()
   212  		if _, err := io.ReadFull(cf, b); err != nil {
   213  			log.Fatal(err)
   214  		}
   215  	}()
   216  }
   217  
   218  func testExec(wg *sync.WaitGroup) {
   219  	wg.Add(1)
   220  	go func() {
   221  		defer wg.Done()
   222  		cmd := exec.Command(os.Args[0], "Block")
   223  		stdin, err := cmd.StdinPipe()
   224  		if err != nil {
   225  			log.Fatal(err)
   226  		}
   227  		cmd.Stderr = new(bytes.Buffer)
   228  		cmd.Stdout = cmd.Stderr
   229  		if err := cmd.Start(); err != nil {
   230  			log.Fatal(err)
   231  		}
   232  
   233  		go func() {
   234  			sendSomeSignals()
   235  			stdin.Close()
   236  		}()
   237  
   238  		if err := cmd.Wait(); err != nil {
   239  			log.Fatalf("%v:\n%s", err, cmd.Stdout)
   240  		}
   241  	}()
   242  }
   243  
   244  // Block blocks until stdin is closed.
   245  func Block() {
   246  	io.Copy(io.Discard, os.Stdin)
   247  }
   248  

View as plain text