github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/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 // 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 }