1 // Copyright 2015 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.
5 // Test broken pipes on Unix systems.
6 //go:build !plan9 && !js
29 func TestEPIPE(t
*testing
.T
) {
30 r
, w
, err
:= os
.Pipe()
34 if err
:= r
.Close(); err
!= nil {
38 expect
:= syscall
.EPIPE
39 if runtime
.GOOS
== "windows" {
40 // 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed".
41 expect
= syscall
.Errno(232)
43 // Every time we write to the pipe we should get an EPIPE.
44 for i
:= 0; i
< 20; i
++ {
45 _
, err
= w
.Write([]byte("hi"))
47 t
.Fatal("unexpected success of Write to broken pipe")
49 if pe
, ok
:= err
.(*fs
.PathError
); ok
{
52 if se
, ok
:= err
.(*os
.SyscallError
); ok
{
56 t
.Errorf("iteration %d: got %v, expected %v", i
, err
, expect
)
61 func TestStdPipe(t
*testing
.T
) {
64 t
.Skip("Windows doesn't support SIGPIPE")
66 testenv
.MustHaveExec(t
)
67 r
, w
, err
:= os
.Pipe()
71 if err
:= r
.Close(); err
!= nil {
74 // Invoke the test program to run the test and write to a closed pipe.
76 // writing to stdout or stderr should cause an immediate SIGPIPE;
77 // writing to descriptor 3 should fail with EPIPE and then exit 0.
79 // all writes should fail with EPIPE and then exit 0.
80 for _
, sig
:= range []bool{false, true} {
81 for dest
:= 1; dest
< 4; dest
++ {
82 cmd
:= osexec
.Command(os
.Args
[0], "-test.run", "TestStdPipeHelper")
85 cmd
.ExtraFiles
= []*os
.File
{w
}
86 cmd
.Env
= append(os
.Environ(), fmt
.Sprintf("GO_TEST_STD_PIPE_HELPER=%d", dest
))
88 cmd
.Env
= append(cmd
.Env
, "GO_TEST_STD_PIPE_HELPER_SIGNAL=1")
90 if err
:= cmd
.Run(); err
== nil {
92 t
.Errorf("unexpected success of write to closed pipe %d sig %t in child", dest
, sig
)
94 } else if ee
, ok
:= err
.(*osexec
.ExitError
); !ok
{
95 t
.Errorf("unexpected exec error type %T: %v", err
, err
)
96 } else if ws
, ok
:= ee
.Sys().(syscall
.WaitStatus
); !ok
{
97 t
.Errorf("unexpected wait status type %T: %v", ee
.Sys(), ee
.Sys())
98 } else if ws
.Signaled() && ws
.Signal() == syscall
.SIGPIPE
{
100 t
.Errorf("unexpected SIGPIPE signal for descriptor %d sig %t", dest
, sig
)
103 t
.Errorf("unexpected exit status %v for descriptor %d sig %t", err
, dest
, sig
)
108 // Test redirecting stdout but not stderr. Issue 40076.
109 cmd
:= osexec
.Command(os
.Args
[0], "-test.run", "TestStdPipeHelper")
111 var stderr bytes
.Buffer
113 cmd
.Env
= append(os
.Environ(), "GO_TEST_STD_PIPE_HELPER=1")
114 if err
:= cmd
.Run(); err
== nil {
115 t
.Errorf("unexpected success of write to closed stdout")
116 } else if ee
, ok
:= err
.(*osexec
.ExitError
); !ok
{
117 t
.Errorf("unexpected exec error type %T: %v", err
, err
)
118 } else if ws
, ok
:= ee
.Sys().(syscall
.WaitStatus
); !ok
{
119 t
.Errorf("unexpected wait status type %T: %v", ee
.Sys(), ee
.Sys())
120 } else if !ws
.Signaled() || ws
.Signal() != syscall
.SIGPIPE
{
121 t
.Errorf("unexpected exit status %v for write to closed stdout", err
)
123 if output
:= stderr
.Bytes(); len(output
) > 0 {
124 t
.Errorf("unexpected output on stderr: %s", output
)
128 // This is a helper for TestStdPipe. It's not a test in itself.
129 func TestStdPipeHelper(t
*testing
.T
) {
130 if os
.Getenv("GO_TEST_STD_PIPE_HELPER_SIGNAL") != "" {
131 signal
.Notify(make(chan os
.Signal
, 1), syscall
.SIGPIPE
)
133 switch os
.Getenv("GO_TEST_STD_PIPE_HELPER") {
135 os
.Stdout
.Write([]byte("stdout"))
137 os
.Stderr
.Write([]byte("stderr"))
139 if _
, err
:= os
.NewFile(3, "3").Write([]byte("3")); err
== nil {
143 t
.Skip("skipping test helper")
145 // For stdout/stderr, we should have crashed with a broken pipe error.
146 // The caller will be looking for that exit status,
147 // so just exit normally here to cause a failure in the caller.
148 // For descriptor 3, a normal exit is expected.
152 func testClosedPipeRace(t
*testing
.T
, read
bool) {
155 // Get the amount we have to write to overload a pipe
158 if b
, err
:= os
.ReadFile("/proc/sys/fs/pipe-max-size"); err
== nil {
159 if i
, err
:= strconv
.Atoi(strings
.TrimSpace(string(b
))); err
== nil {
163 t
.Logf("using pipe write limit of %d", limit
)
166 r
, w
, err
:= os
.Pipe()
173 // Close the read end of the pipe in a goroutine while we are
174 // writing to the write end, or vice-versa.
176 // Give the main goroutine a chance to enter the Read or
177 // Write call. This is sloppy but the test will pass even
178 // if we close before the read/write.
179 time
.Sleep(20 * time
.Millisecond
)
192 b
:= make([]byte, limit
)
194 _
, err
= r
.Read(b
[:])
196 _
, err
= w
.Write(b
[:])
199 t
.Error("I/O on closed pipe unexpectedly succeeded")
200 } else if pe
, ok
:= err
.(*fs
.PathError
); !ok
{
201 t
.Errorf("I/O on closed pipe returned unexpected error type %T; expected fs.PathError", pe
)
202 } else if pe
.Err
!= fs
.ErrClosed
{
203 t
.Errorf("got error %q but expected %q", pe
.Err
, fs
.ErrClosed
)
205 t
.Logf("I/O returned expected error %q", err
)
209 func TestClosedPipeRaceRead(t
*testing
.T
) {
210 testClosedPipeRace(t
, true)
213 func TestClosedPipeRaceWrite(t
*testing
.T
) {
214 testClosedPipeRace(t
, false)
217 // Issue 20915: Reading on nonblocking fd should not return "waiting
218 // for unsupported file type." Currently it returns EAGAIN; it is
219 // possible that in the future it will simply wait for data.
220 func TestReadNonblockingFd(t
*testing
.T
) {
221 switch runtime
.GOOS
{
223 t
.Skip("Windows doesn't support SetNonblock")
225 if os
.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" {
226 fd
:= syscallDescriptor(os
.Stdin
.Fd())
227 syscall
.SetNonblock(fd
, true)
228 defer syscall
.SetNonblock(fd
, false)
229 _
, err
:= os
.Stdin
.Read(make([]byte, 1))
231 if perr
, ok
:= err
.(*fs
.PathError
); !ok || perr
.Err
!= syscall
.EAGAIN
{
232 t
.Fatalf("read on nonblocking stdin got %q, should have gotten EAGAIN", err
)
238 testenv
.MustHaveExec(t
)
239 r
, w
, err
:= os
.Pipe()
245 cmd
:= osexec
.Command(os
.Args
[0], "-test.run="+t
.Name())
246 cmd
.Env
= append(os
.Environ(), "GO_WANT_READ_NONBLOCKING_FD=1")
248 output
, err
:= cmd
.CombinedOutput()
251 t
.Errorf("child process failed: %v", err
)
255 func TestCloseWithBlockingReadByNewFile(t
*testing
.T
) {
256 var p
[2]syscallDescriptor
257 err
:= syscall
.Pipe(p
[:])
261 // os.NewFile returns a blocking mode file.
262 testCloseWithBlockingRead(t
, os
.NewFile(uintptr(p
[0]), "reader"), os
.NewFile(uintptr(p
[1]), "writer"))
265 func TestCloseWithBlockingReadByFd(t
*testing
.T
) {
266 r
, w
, err
:= os
.Pipe()
270 // Calling Fd will put the file into blocking mode.
272 testCloseWithBlockingRead(t
, r
, w
)
275 // Test that we don't let a blocking read prevent a close.
276 func testCloseWithBlockingRead(t
*testing
.T
, r
, w
*os
.File
) {
280 c1
, c2
:= make(chan bool), make(chan bool)
281 var wg sync
.WaitGroup
284 go func(c
chan bool) {
286 // Give the other goroutine a chance to enter the Read
287 // or Write call. This is sloppy but the test will
288 // pass even if we close before the read/write.
289 time
.Sleep(20 * time
.Millisecond
)
291 if err
:= r
.Close(); err
!= nil {
298 go func(c
chan bool) {
301 _
, err
:= r
.Read(b
[:])
304 t
.Error("I/O on closed pipe unexpectedly succeeded")
306 if pe
, ok
:= err
.(*fs
.PathError
); ok
{
309 if err
!= io
.EOF
&& err
!= fs
.ErrClosed
{
310 t
.Errorf("got %v, expected EOF or closed", err
)
314 for c1
!= nil || c2
!= nil {
318 // r.Close has completed, but the blocking Read
319 // is hanging. Close the writer to unblock it.
323 case <-time
.After(1 * time
.Second
):
325 case c1
!= nil && c2
!= nil:
326 t
.Error("timed out waiting for Read and Close")
329 t
.Error("timed out waiting for Close")
331 t
.Error("timed out waiting for Read")
333 t
.Error("impossible case")
341 // Issue 24164, for pipes.
342 func TestPipeEOF(t
*testing
.T
) {
343 r
, w
, err
:= os
.Pipe()
348 var wg sync
.WaitGroup
354 if err
:= w
.Close(); err
!= nil {
355 t
.Errorf("error closing writer: %v", err
)
359 for i
:= 0; i
< 3; i
++ {
360 time
.Sleep(10 * time
.Millisecond
)
361 _
, err
:= fmt
.Fprintf(w
, "line %d\n", i
)
363 t
.Errorf("error writing to fifo: %v", err
)
367 time
.Sleep(10 * time
.Millisecond
)
372 done
:= make(chan bool)
377 if err
:= r
.Close(); err
!= nil {
378 t
.Errorf("error closing reader: %v", err
)
382 rbuf
:= bufio
.NewReader(r
)
384 b
, err
:= rbuf
.ReadBytes('\n')
392 t
.Logf("%s\n", bytes
.TrimSpace(b
))
399 case <-time
.After(time
.Second
):
400 t
.Error("timed out waiting for read")
401 // Close the reader to force the read to complete.
407 func TestFdRace(t
*testing
.T
) {
408 r
, w
, err
:= os
.Pipe()
415 var wg sync
.WaitGroup
422 for i
:= 0; i
< tries
; i
++ {
429 func TestFdReadRace(t
*testing
.T
) {
432 r
, w
, err
:= os
.Pipe()
441 c
:= make(chan bool, 1)
442 var wg sync
.WaitGroup
447 r
.SetReadDeadline(time
.Now().Add(time
.Minute
))
449 if _
, err
:= r
.Read(buf
[:]); os
.IsTimeout(err
) {
450 t
.Error("read timed out")
458 // Give the other goroutine a chance to enter the Read.
459 // It doesn't matter if this occasionally fails, the test
460 // will still pass, it just won't test anything.
461 time
.Sleep(10 * time
.Millisecond
)
464 // The bug was that Fd would hang until Read timed out.
465 // If the bug is fixed, then writing to w and closing r here
466 // will cause the Read to exit before the timeout expires.
467 w
.Write(make([]byte, count
))