// Copyright 2020 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !plan9 && !windows // +build !plan9,!windows package main /* #include #include #include static int clearRestart(int sig) { struct sigaction sa; memset(&sa, 0, sizeof sa); if (sigaction(sig, NULL, &sa) < 0) { return errno; } sa.sa_flags &=~ SA_RESTART; if (sigaction(sig, &sa, NULL) < 0) { return errno; } return 0; } */ import "C" import ( "bytes" "errors" "fmt" "io" "log" "net" "os" "os/exec" "sync" "syscall" "time" ) func init() { register("EINTR", EINTR) register("Block", Block) } // Test various operations when a signal handler is installed without // the SA_RESTART flag. This tests that the os and net APIs handle EINTR. func EINTR() { if errno := C.clearRestart(C.int(syscall.SIGURG)); errno != 0 { log.Fatal(syscall.Errno(errno)) } if errno := C.clearRestart(C.int(syscall.SIGWINCH)); errno != 0 { log.Fatal(syscall.Errno(errno)) } if errno := C.clearRestart(C.int(syscall.SIGCHLD)); errno != 0 { log.Fatal(syscall.Errno(errno)) } var wg sync.WaitGroup testPipe(&wg) testNet(&wg) testExec(&wg) wg.Wait() fmt.Println("OK") } // spin does CPU bound spinning and allocating for a millisecond, // to get a SIGURG. //go:noinline func spin() (float64, []byte) { stop := time.Now().Add(time.Millisecond) r1 := 0.0 r2 := make([]byte, 200) for time.Now().Before(stop) { for i := 1; i < 1e6; i++ { r1 += r1 / float64(i) r2 = append(r2, bytes.Repeat([]byte{byte(i)}, 100)...) r2 = r2[100:] } } return r1, r2 } // winch sends a few SIGWINCH signals to the process. func winch() { ticker := time.NewTicker(100 * time.Microsecond) defer ticker.Stop() pid := syscall.Getpid() for n := 10; n > 0; n-- { syscall.Kill(pid, syscall.SIGWINCH) <-ticker.C } } // sendSomeSignals triggers a few SIGURG and SIGWINCH signals. func sendSomeSignals() { done := make(chan struct{}) go func() { spin() close(done) }() winch() <-done } // testPipe tests pipe operations. func testPipe(wg *sync.WaitGroup) { r, w, err := os.Pipe() if err != nil { log.Fatal(err) } if err := syscall.SetNonblock(int(r.Fd()), false); err != nil { log.Fatal(err) } if err := syscall.SetNonblock(int(w.Fd()), false); err != nil { log.Fatal(err) } wg.Add(2) go func() { defer wg.Done() defer w.Close() // Spin before calling Write so that the first ReadFull // in the other goroutine will likely be interrupted // by a signal. sendSomeSignals() // This Write will likely be interrupted by a signal // as the other goroutine spins in the middle of reading. // We write enough data that we should always fill the // pipe buffer and need multiple write system calls. if _, err := w.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil { log.Fatal(err) } }() go func() { defer wg.Done() defer r.Close() b := make([]byte, 1<<20) // This ReadFull will likely be interrupted by a signal, // as the other goroutine spins before writing anything. if _, err := io.ReadFull(r, b); err != nil { log.Fatal(err) } // Spin after reading half the data so that the Write // in the other goroutine will likely be interrupted // before it completes. sendSomeSignals() if _, err := io.ReadFull(r, b); err != nil { log.Fatal(err) } }() } // testNet tests network operations. func testNet(wg *sync.WaitGroup) { ln, err := net.Listen("tcp4", "127.0.0.1:0") if err != nil { if errors.Is(err, syscall.EAFNOSUPPORT) || errors.Is(err, syscall.EPROTONOSUPPORT) { return } log.Fatal(err) } wg.Add(2) go func() { defer wg.Done() defer ln.Close() c, err := ln.Accept() if err != nil { log.Fatal(err) } defer c.Close() cf, err := c.(*net.TCPConn).File() if err != nil { log.Fatal(err) } defer cf.Close() if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil { log.Fatal(err) } // See comments in testPipe. sendSomeSignals() if _, err := cf.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil { log.Fatal(err) } }() go func() { defer wg.Done() sendSomeSignals() c, err := net.Dial("tcp", ln.Addr().String()) if err != nil { log.Fatal(err) } defer c.Close() cf, err := c.(*net.TCPConn).File() if err != nil { log.Fatal(err) } defer cf.Close() if err := syscall.SetNonblock(int(cf.Fd()), false); err != nil { log.Fatal(err) } // See comments in testPipe. b := make([]byte, 1<<20) if _, err := io.ReadFull(cf, b); err != nil { log.Fatal(err) } sendSomeSignals() if _, err := io.ReadFull(cf, b); err != nil { log.Fatal(err) } }() } func testExec(wg *sync.WaitGroup) { wg.Add(1) go func() { defer wg.Done() cmd := exec.Command(os.Args[0], "Block") stdin, err := cmd.StdinPipe() if err != nil { log.Fatal(err) } cmd.Stderr = new(bytes.Buffer) cmd.Stdout = cmd.Stderr if err := cmd.Start(); err != nil { log.Fatal(err) } go func() { sendSomeSignals() stdin.Close() }() if err := cmd.Wait(); err != nil { log.Fatalf("%v:\n%s", err, cmd.Stdout) } }() } // Block blocks until stdin is closed. func Block() { io.Copy(io.Discard, os.Stdin) }