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
8package main
9
10/*
11#include <errno.h>
12#include <signal.h>
13#include <string.h>
14
15static 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*/
29import "C"
30
31import (
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
45func 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.
52func 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
75func 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.
90func 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.
101func 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.
112func 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.
159func 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
218func 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.
245func Block() {
246	io.Copy(io.Discard, os.Stdin)
247}
248