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  //go:noinline
    74  func spin() (float64, []byte) {
    75  	stop := time.Now().Add(time.Millisecond)
    76  	r1 := 0.0
    77  	r2 := make([]byte, 200)
    78  	for time.Now().Before(stop) {
    79  		for i := 1; i < 1e6; i++ {
    80  			r1 += r1 / float64(i)
    81  			r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)...)
    82  			r2 = r2[100:]
    83  		}
    84  	}
    85  	return r1, r2
    86  }
    87  
    88  // winch sends a few SIGWINCH signals to the process.
    89  func winch() {
    90  	ticker := time.NewTicker(100 * time.Microsecond)
    91  	defer ticker.Stop()
    92  	pid := syscall.Getpid()
    93  	for n := 10; n > 0; n-- {
    94  		syscall.Kill(pid, syscall.SIGWINCH)
    95  		<-ticker.C
    96  	}
    97  }
    98  
    99  // sendSomeSignals triggers a few SIGURG and SIGWINCH signals.
   100  func sendSomeSignals() {
   101  	done := make(chan struct{})
   102  	go func() {
   103  		spin()
   104  		close(done)
   105  	}()
   106  	winch()
   107  	<-done
   108  }
   109  
   110  // testPipe tests pipe operations.
   111  func testPipe(wg *sync.WaitGroup) {
   112  	r, w, err := os.Pipe()
   113  	if err != nil {
   114  		log.Fatal(err)
   115  	}
   116  	if err := syscall.SetNonblock(int(r.Fd()), false); err != nil {
   117  		log.Fatal(err)
   118  	}
   119  	if err := syscall.SetNonblock(int(w.Fd()), false); err != nil {
   120  		log.Fatal(err)
   121  	}
   122  	wg.Add(2)
   123  	go func() {
   124  		defer wg.Done()
   125  		defer w.Close()
   126  		// Spin before calling Write so that the first ReadFull
   127  		// in the other goroutine will likely be interrupted
   128  		// by a signal.
   129  		sendSomeSignals()
   130  		// This Write will likely be interrupted by a signal
   131  		// as the other goroutine spins in the middle of reading.
   132  		// We write enough data that we should always fill the
   133  		// pipe buffer and need multiple write system calls.
   134  		if _, err := w.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil {
   135  			log.Fatal(err)
   136  		}
   137  	}()
   138  	go func() {
   139  		defer wg.Done()
   140  		defer r.Close()
   141  		b := make([]byte, 1<<20)
   142  		// This ReadFull will likely be interrupted by a signal,
   143  		// as the other goroutine spins before writing anything.
   144  		if _, err := io.ReadFull(r, b); err != nil {
   145  			log.Fatal(err)
   146  		}
   147  		// Spin after reading half the data so that the Write
   148  		// in the other goroutine will likely be interrupted
   149  		// before it completes.
   150  		sendSomeSignals()
   151  		if _, err := io.ReadFull(r, b); err != nil {
   152  			log.Fatal(err)
   153  		}
   154  	}()
   155  }
   156  
   157  // testNet tests network operations.
   158  func testNet(wg *sync.WaitGroup) {
   159  	ln, err := net.Listen("tcp4", "127.0.0.1:0")
   160  	if err != nil {
   161  		if errors.Is(err, syscall.EAFNOSUPPORT) || errors.Is(err, syscall.EPROTONOSUPPORT) {
   162  			return
   163  		}
   164  		log.Fatal(err)
   165  	}
   166  	wg.Add(2)
   167  	go func() {
   168  		defer wg.Done()
   169  		defer ln.Close()
   170  		c, err := ln.Accept()
   171  		if err != nil {
   172  			log.Fatal(err)
   173  		}
   174  		defer c.Close()
   175  		cf, err := c.(*net.TCPConn).File()
   176  		if err != nil {
   177  			log.Fatal(err)
   178  		}
   179  		defer cf.Close()
   180  		if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil {
   181  			log.Fatal(err)
   182  		}
   183  		// See comments in testPipe.
   184  		sendSomeSignals()
   185  		if _, err := cf.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil {
   186  			log.Fatal(err)
   187  		}
   188  	}()
   189  	go func() {
   190  		defer wg.Done()
   191  		sendSomeSignals()
   192  		c, err := net.Dial("tcp", ln.Addr().String())
   193  		if err != nil {
   194  			log.Fatal(err)
   195  		}
   196  		defer c.Close()
   197  		cf, err := c.(*net.TCPConn).File()
   198  		if err != nil {
   199  			log.Fatal(err)
   200  		}
   201  		defer cf.Close()
   202  		if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil {
   203  			log.Fatal(err)
   204  		}
   205  		// See comments in testPipe.
   206  		b := make([]byte, 1<<20)
   207  		if _, err := io.ReadFull(cf, b); err != nil {
   208  			log.Fatal(err)
   209  		}
   210  		sendSomeSignals()
   211  		if _, err := io.ReadFull(cf, b); err != nil {
   212  			log.Fatal(err)
   213  		}
   214  	}()
   215  }
   216  
   217  func testExec(wg *sync.WaitGroup) {
   218  	wg.Add(1)
   219  	go func() {
   220  		defer wg.Done()
   221  		cmd := exec.Command(os.Args[0], "Block")
   222  		stdin, err := cmd.StdinPipe()
   223  		if err != nil {
   224  			log.Fatal(err)
   225  		}
   226  		cmd.Stderr = new(bytes.Buffer)
   227  		cmd.Stdout = cmd.Stderr
   228  		if err := cmd.Start(); err != nil {
   229  			log.Fatal(err)
   230  		}
   231  
   232  		go func() {
   233  			sendSomeSignals()
   234  			stdin.Close()
   235  		}()
   236  
   237  		if err := cmd.Wait(); err != nil {
   238  			log.Fatalf("%v:\n%s", err, cmd.Stdout)
   239  		}
   240  	}()
   241  }
   242  
   243  // Block blocks until stdin is closed.
   244  func Block() {
   245  	io.Copy(io.Discard, os.Stdin)
   246  }
   247  

View as plain text