Skip to content
Snippets Groups Projects
Commit b822f098 authored by Peter Collingbourne's avatar Peter Collingbourne Committed by Gopher Robot
Browse files

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: default avatarIan Lance Taylor <iant@google.com>
parent bedda245
No related branches found
No related tags found
No related merge requests found
...@@ -115,7 +115,7 @@ var testingForceReadDirLstat bool ...@@ -115,7 +115,7 @@ var testingForceReadDirLstat bool
// ReadDir returns the entries it was able to read before the error, // ReadDir returns the entries it was able to read before the error,
// along with the error. // along with the error.
func ReadDir(name string) ([]DirEntry, error) { func ReadDir(name string) ([]DirEntry, error) {
f, err := Open(name) f, err := openDir(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -380,6 +380,14 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) { ...@@ -380,6 +380,14 @@ func OpenFile(name string, flag int, perm FileMode) (*File, error) {
return f, nil 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. // lstat is overridden in tests.
var lstat = Lstat var lstat = Lstat
......
...@@ -139,6 +139,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { ...@@ -139,6 +139,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
return NewFile(uintptr(fd), name), nil 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. // Close closes the File, rendering it unusable for I/O.
// On files that support SetDeadline, any pending I/O operations will // On files that support SetDeadline, any pending I/O operations will
// be canceled and return immediately with an ErrClosed error. // be canceled and return immediately with an ErrClosed error.
......
...@@ -157,7 +157,7 @@ const ( ...@@ -157,7 +157,7 @@ const (
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.
// Used by openFdAt for directories. // Used by openFdAt and openDirNolog for directories.
kindNoPoll kindNoPoll
) )
...@@ -256,7 +256,7 @@ func epipecheck(file *File, e error) { ...@@ -256,7 +256,7 @@ func epipecheck(file *File, e error) {
const DevNull = "/dev/null" const DevNull = "/dev/null"
// openFileNolog is the Unix implementation of OpenFile. // 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) { func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
setSticky := false setSticky := false
if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 { if !supportsCreateWithStickyBit && flag&O_CREATE != 0 && perm&ModeSticky != 0 {
...@@ -303,6 +303,32 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { ...@@ -303,6 +303,32 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
return f, nil 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 { func (file *file) close() error {
if file == nil { if file == nil {
return syscall.EINVAL return syscall.EINVAL
......
...@@ -119,6 +119,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) { ...@@ -119,6 +119,10 @@ func openFileNolog(name string, flag int, perm FileMode) (*File, error) {
return newFile(r, name, "file"), nil return newFile(r, name, "file"), nil
} }
func openDirNolog(name string) (*File, error) {
return openFileNolog(name, O_RDONLY, 0)
}
func (file *file) close() error { func (file *file) close() error {
if file == nil { if file == nil {
return syscall.EINVAL return syscall.EINVAL
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment