github.com/geraldss/go/src@v0.0.0-20210511222824-ac7d0ebfc235/runtime/testdata/testprogcgo/eintr.go (about)

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