kindFile
kindConsole
kindDir
+ kindPipe
)
// logInitFD is set by tests to enable file descriptor initialization logging.
fd.kind = kindConsole
case "dir":
fd.kind = kindDir
+ case "pipe":
+ fd.kind = kindPipe
case "tcp", "tcp4", "tcp6",
"udp", "udp4", "udp6",
"ip", "ip4", "ip6",
if !fd.fdmu.increfAndClose() {
return errClosing(fd.isFile)
}
+ if fd.kind == kindPipe {
+ syscall.CancelIoEx(fd.Sysfd, nil)
+ }
// unblock pending reader and writer
fd.pd.evict()
err := fd.decref()
n, err = fd.readConsole(buf)
default:
n, err = syscall.Read(fd.Sysfd, buf)
+ if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED {
+ // Close uses CancelIoEx to interrupt concurrent I/O for pipes.
+ // If the fd is a pipe and the Read was interrupted by CancelIoEx,
+ // we assume it is interrupted by Close.
+ err = ErrFileClosing
+ }
}
if err != nil {
n = 0
n, err = fd.writeConsole(b)
default:
n, err = syscall.Write(fd.Sysfd, b)
+ if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED {
+ // Close uses CancelIoEx to interrupt concurrent I/O for pipes.
+ // If the fd is a pipe and the Write was interrupted by CancelIoEx,
+ // we assume it is interrupted by Close.
+ err = ErrFileClosing
+ }
}
if err != nil {
n = 0
if syscall.GetConsoleMode(h, &m) == nil {
kind = "console"
}
+ if t, err := syscall.GetFileType(h); err == nil && t == syscall.FILE_TYPE_PIPE {
+ kind = "pipe"
+ }
}
f := &File{&file{
if e != nil {
return nil, nil, NewSyscallError("pipe", e)
}
- return newFile(p[0], "|0", "file"), newFile(p[1], "|1", "file"), nil
+ return newFile(p[0], "|0", "pipe"), newFile(p[1], "|1", "pipe"), nil
}
func tempDir() string {
// license that can be found in the LICENSE file.
// Test broken pipes on Unix systems.
-// +build !windows,!plan9,!nacl,!js
+// +build !plan9,!nacl,!js
package os_test
t.Fatal(err)
}
+ expect := syscall.EPIPE
+ if runtime.GOOS == "windows" {
+ // 232 is Windows error code ERROR_NO_DATA, "The pipe is being closed".
+ expect = syscall.Errno(232)
+ }
// Every time we write to the pipe we should get an EPIPE.
for i := 0; i < 20; i++ {
_, err = w.Write([]byte("hi"))
if se, ok := err.(*os.SyscallError); ok {
err = se.Err
}
- if err != syscall.EPIPE {
- t.Errorf("iteration %d: got %v, expected EPIPE", i, err)
+ if err != expect {
+ t.Errorf("iteration %d: got %v, expected %v", i, err, expect)
}
}
}
func TestStdPipe(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skip("Windows doesn't support SIGPIPE")
+ }
testenv.MustHaveExec(t)
r, w, err := os.Pipe()
if err != nil {
// for unsupported file type." Currently it returns EAGAIN; it is
// possible that in the future it will simply wait for data.
func TestReadNonblockingFd(t *testing.T) {
+ switch runtime.GOOS {
+ case "windows":
+ t.Skip("Windows doesn't support SetNonblock")
+ }
if os.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" {
- fd := int(os.Stdin.Fd())
+ fd := syscallDescriptor(os.Stdin.Fd())
syscall.SetNonblock(fd, true)
defer syscall.SetNonblock(fd, false)
_, err := os.Stdin.Read(make([]byte, 1))
}
func TestCloseWithBlockingReadByNewFile(t *testing.T) {
- var p [2]int
+ var p [2]syscallDescriptor
err := syscall.Pipe(p[:])
if err != nil {
t.Fatal(err)
if err == nil {
t.Error("I/O on closed pipe unexpectedly succeeded")
}
- if err != io.EOF {
- t.Errorf("got %v, expected io.EOF", err)
+ if pe, ok := err.(*os.PathError); ok {
+ err = pe.Err
+ }
+ if err != io.EOF && err != os.ErrClosed {
+ t.Errorf("got %v, expected EOF or closed", err)
}
}(c2)