mirror of https://github.com/golang/go.git
os: don't try to make the directory FD non-blocking in os.ReadDir
This will fail because epoll_ctl() fails on directory FDs, so we end up issuing unnecessary syscalls. My test program that calls filepath.WalkDir on a large directory tree runs 1.23 ± 0.04 times faster than with the original implementation. Change-Id: Ie33d798c48057a7b2d0bacac80fcdde5b5a8bb1b Reviewed-on: https://go-review.googlesource.com/c/go/+/570877 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Ian Lance Taylor <iant@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
parent
bedda24574
commit
b822f098c5
|
|
@ -115,7 +115,7 @@ var testingForceReadDirLstat bool
|
|||
// ReadDir returns the entries it was able to read before the error,
|
||||
// along with the error.
|
||||
func ReadDir(name string) ([]DirEntry, error) {
|
||||
f, err := Open(name)
|
||||
f, err := openDir(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -380,6 +380,14 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
|
|||
return f, nil
|
||||
}
|
||||
|
||||
// openDir opens a file which is assumed to be a directory. As such, it skips
|
||||
// the syscalls that make the file descriptor non-blocking as these take time
|
||||
// and will fail on file descriptors for directories.
|
||||
func openDir(name string) (*File, error) {
|
||||
testlog.Open(name)
|
||||
return openDirNolog(name)
|
||||
}
|
||||
|
||||
// lstat is overridden in tests.
|
||||
var lstat = Lstat
|
||||
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
|||
return NewFile(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
func openDirNolog(name string) (*File, error) {
|
||||
return openFileNolog(name, O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// Close closes the File, rendering it unusable for I/O.
|
||||
// On files that support SetDeadline, any pending I/O operations will
|
||||
// be canceled and return immediately with an ErrClosed error.
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ const (
|
|||
kindNonBlock
|
||||
// kindNoPoll means that we should not put the descriptor into
|
||||
// non-blocking mode, because we know it is not a pipe or FIFO.
|
||||
// Used by openFdAt for directories.
|
||||
// Used by openFdAt and openDirNolog for directories.
|
||||
kindNoPoll
|
||||
)
|
||||
|
||||
|
|
@ -256,7 +256,7 @@ func epipecheck(file *File, e error) {
|
|||
const DevNull = "/dev/null"
|
||||
|
||||
// openFileNolog is the Unix implementation of OpenFile.
|
||||
// Changes here should be reflected in openFdAt, if relevant.
|
||||
// Changes here should be reflected in openFdAt and openDirNolog, if relevant.
|
||||
func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
||||
setSticky := false
|
||||
if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
|
||||
|
|
@ -303,6 +303,32 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
|||
return f, nil
|
||||
}
|
||||
|
||||
func openDirNolog(name string) (*File, error) {
|
||||
var r int
|
||||
var s poll.SysFile
|
||||
for {
|
||||
var e error
|
||||
r, s, e = open(name, O_RDONLY|syscall.O_CLOEXEC, 0)
|
||||
if e == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if e == syscall.EINTR {
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, &PathError{Op: "open", Path: name, Err: e}
|
||||
}
|
||||
|
||||
if !supportsCloseOnExec {
|
||||
syscall.CloseOnExec(r)
|
||||
}
|
||||
|
||||
f := newFile(r, name, kindNoPoll)
|
||||
f.pfd.SysFile = s
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (file *file) close() error {
|
||||
if file == nil {
|
||||
return syscall.EINVAL
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
|
|||
return newFile(r, name, "file"), nil
|
||||
}
|
||||
|
||||
func openDirNolog(name string) (*File, error) {
|
||||
return openFileNolog(name, O_RDONLY, 0)
|
||||
}
|
||||
|
||||
func (file *file) close() error {
|
||||
if file == nil {
|
||||
return syscall.EINVAL
|
||||
|
|
|
|||
Loading…
Reference in New Issue