1
0
mirror of https://github.com/golang/go synced 2024-11-17 02:54:45 -07:00

os: if descriptor is non-blocking, retain that in Fd method

For #58408
Fixes #60211

Change-Id: I30f5678b46e15121865b19d1c0f82698493fad4e
Reviewed-on: https://go-review.googlesource.com/c/go/+/495079
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Ian Lance Taylor 2023-05-15 21:50:51 -07:00 committed by Gopher Robot
parent f0de4b4f03
commit f777726ff0
6 changed files with 78 additions and 5 deletions

View File

@ -19,3 +19,7 @@ func IsNonblock(fd int) (nonblocking bool, err error) {
} }
return flag&syscall.O_NONBLOCK != 0, nil return flag&syscall.O_NONBLOCK != 0, nil
} }
func HasNonblockFlag(flag int) bool {
return flag&syscall.O_NONBLOCK != 0
}

View File

@ -9,3 +9,7 @@ package unix
func IsNonblock(fd int) (nonblocking bool, err error) { func IsNonblock(fd int) (nonblocking bool, err error) {
return false, nil return false, nil
} }
func HasNonblockFlag(flag int) bool {
return false
}

View File

@ -19,6 +19,10 @@ func IsNonblock(fd int) (nonblocking bool, err error) {
return flag&syscall.O_NONBLOCK != 0, nil return flag&syscall.O_NONBLOCK != 0, nil
} }
func HasNonblockFlag(flag int) bool {
return flag&syscall.O_NONBLOCK != 0
}
// Implemented in the syscall package. // Implemented in the syscall package.
// //
//go:linkname fcntl syscall.fcntl //go:linkname fcntl syscall.fcntl

View File

@ -19,6 +19,10 @@ func IsNonblock(fd int) (nonblocking bool, err error) {
return flags&syscall.FDFLAG_NONBLOCK != 0, nil return flags&syscall.FDFLAG_NONBLOCK != 0, nil
} }
func HasNonblockFlag(flag int) bool {
return flag&syscall.FDFLAG_NONBLOCK != 0
}
// This helper is implemented in the syscall package. It means we don't have // This helper is implemented in the syscall package. It means we don't have
// to redefine the fd_fdstat_get host import or the fdstat struct it // to redefine the fd_fdstat_get host import or the fdstat struct it
// populates. // populates.

View File

@ -8,6 +8,7 @@ package os_test
import ( import (
"errors" "errors"
"internal/syscall/unix"
"internal/testenv" "internal/testenv"
"io/fs" "io/fs"
"os" "os"
@ -155,3 +156,52 @@ func TestNonPollable(t *testing.T) {
} }
} }
} }
// Issue 60211.
func TestOpenFileNonBlocking(t *testing.T) {
exe, err := os.Executable()
if err != nil {
t.Skipf("can't find executable: %v", err)
}
f, err := os.OpenFile(exe, os.O_RDONLY|syscall.O_NONBLOCK, 0666)
if err != nil {
t.Fatal(err)
}
defer f.Close()
nonblock, err := unix.IsNonblock(int(f.Fd()))
if err != nil {
t.Fatal(err)
}
if !nonblock {
t.Errorf("file opened with O_NONBLOCK but in blocking mode")
}
}
func TestNewFileNonBlocking(t *testing.T) {
var p [2]int
if err := syscall.Pipe(p[:]); err != nil {
t.Fatal(err)
}
if err := syscall.SetNonblock(p[0], true); err != nil {
t.Fatal(err)
}
f := os.NewFile(uintptr(p[0]), "pipe")
nonblock, err := unix.IsNonblock(p[0])
if err != nil {
t.Fatal(err)
}
if !nonblock {
t.Error("pipe blocking after NewFile")
}
fd := f.Fd()
if fd != uintptr(p[0]) {
t.Errorf("Fd returned %d, want %d", fd, p[0])
}
nonblock, err = unix.IsNonblock(p[0])
if err != nil {
t.Fatal(err)
}
if !nonblock {
t.Error("pipe blocking after Fd")
}
}

View File

@ -116,12 +116,12 @@ const (
// kindNewFile means that the descriptor was passed to us via NewFile. // kindNewFile means that the descriptor was passed to us via NewFile.
kindNewFile newFileKind = iota kindNewFile newFileKind = iota
// kindOpenFile means that the descriptor was opened using // kindOpenFile means that the descriptor was opened using
// Open, Create, or OpenFile. // Open, Create, or OpenFile (without O_NONBLOCK).
kindOpenFile kindOpenFile
// kindPipe means that the descriptor was opened using Pipe. // kindPipe means that the descriptor was opened using Pipe.
kindPipe kindPipe
// kindNonBlock means that the descriptor was passed to us via NewFile, // kindNonBlock means that the descriptor is already in
// and the descriptor is already in non-blocking mode. // non-blocking mode.
kindNonBlock kindNonBlock
// kindNoPoll means that we should not put the descriptor into // kindNoPoll means that we should not put the descriptor into
// non-blocking mode, because we know it is not a pipe or FIFO. // non-blocking mode, because we know it is not a pipe or FIFO.
@ -184,7 +184,9 @@ func newFile(fd uintptr, name string, kind newFileKind) *File {
clearNonBlock := false clearNonBlock := false
if pollable { if pollable {
if kind == kindNonBlock { if kind == kindNonBlock {
f.nonblock = true // The descriptor is already in non-blocking mode.
// We only set f.nonblock if we put the file into
// non-blocking mode.
} else if err := syscall.SetNonblock(fdi, true); err == nil { } else if err := syscall.SetNonblock(fdi, true); err == nil {
f.nonblock = true f.nonblock = true
clearNonBlock = true clearNonBlock = true
@ -263,7 +265,12 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
syscall.CloseOnExec(r) syscall.CloseOnExec(r)
} }
f := newFile(uintptr(r), name, kindOpenFile) kind := kindOpenFile
if unix.HasNonblockFlag(flag) {
kind = kindNonBlock
}
f := newFile(uintptr(r), name, kind)
f.pfd.SysFile = s f.pfd.SysFile = s
return f, nil return f, nil
} }