github.com/m10x/go/src@v0.0.0-20220112094212-ba61592315da/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 //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 }