diff options
author | Ian Lance Taylor <iant@golang.org> | 2017-07-13 15:07:07 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2017-07-14 04:03:31 +0000 |
commit | 26f0a7af456b3743e718002534576c6ef1ad99a3 (patch) | |
tree | de706c3cad0b857667f3b573795c3ce9d71846e4 | |
parent | 4c98ecbf05d1baba49607a6e168a408787cf77d6 (diff) | |
download | go-26f0a7af456b3743e718002534576c6ef1ad99a3.tar.gz go-26f0a7af456b3743e718002534576c6ef1ad99a3.zip |
internal/poll: don't wait for unpollable files
If we get an EAGAIN error on an unpollable file, don't try to wait for
it to be ready; just return EAGAIN.
It's possible that we should instead ensure that when Stdin is a pipe
in non-blocking mode, we wait for data to appear. For now take the
conservative approach of doing what we did in previous releases.
Based on https://golang.org/cl/47555 by Totoro W.
Fixes #20915
Change-Id: Icc9e97a5a877b0a3583ec056c35412d1afab62d1
Reviewed-on: https://go-review.googlesource.com/48490
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
-rw-r--r-- | src/internal/poll/fd_poll_nacl.go | 2 | ||||
-rw-r--r-- | src/internal/poll/fd_poll_runtime.go | 4 | ||||
-rw-r--r-- | src/internal/poll/fd_unix.go | 20 | ||||
-rw-r--r-- | src/os/pipe_test.go | 34 |
4 files changed, 51 insertions, 9 deletions
diff --git a/src/internal/poll/fd_poll_nacl.go b/src/internal/poll/fd_poll_nacl.go index 8fa75c5a26..2df30030b1 100644 --- a/src/internal/poll/fd_poll_nacl.go +++ b/src/internal/poll/fd_poll_nacl.go @@ -49,6 +49,8 @@ func (pd *pollDesc) waitWrite(isFile bool) error { return pd.wait('w', isFile) } func (pd *pollDesc) waitCanceled(mode int) {} +func (pd *pollDesc) pollable() bool { return true } + // SetDeadline sets the read and write deadlines associated with fd. func (fd *FD) SetDeadline(t time.Time) error { return setDeadlineImpl(fd, t, 'r'+'w') diff --git a/src/internal/poll/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go index 08b40c2720..bfbe3c7de4 100644 --- a/src/internal/poll/fd_poll_runtime.go +++ b/src/internal/poll/fd_poll_runtime.go @@ -101,6 +101,10 @@ func (pd *pollDesc) waitCanceled(mode int) { runtime_pollWaitCanceled(pd.runtimeCtx, mode) } +func (pd *pollDesc) pollable() bool { + return pd.runtimeCtx != 0 +} + func convertErr(res int, isFile bool) error { switch res { case 0: diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 3ca6e157c5..c40c701f59 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -121,7 +121,7 @@ func (fd *FD) Read(p []byte) (int, error) { n, err := syscall.Read(fd.Sysfd, p) if err != nil { n = 0 - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitRead(fd.isFile); err == nil { continue } @@ -165,7 +165,7 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) { n, sa, err := syscall.Recvfrom(fd.Sysfd, p, 0) if err != nil { n = 0 - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitRead(fd.isFile); err == nil { continue } @@ -189,7 +189,7 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er n, oobn, flags, sa, err := syscall.Recvmsg(fd.Sysfd, p, oob, 0) if err != nil { // TODO(dfc) should n and oobn be set to 0 - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitRead(fd.isFile); err == nil { continue } @@ -222,7 +222,7 @@ func (fd *FD) Write(p []byte) (int, error) { if nn == len(p) { return nn, err } - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitWrite(fd.isFile); err == nil { continue } @@ -278,7 +278,7 @@ func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (int, error) { } for { err := syscall.Sendto(fd.Sysfd, p, 0, sa) - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitWrite(fd.isFile); err == nil { continue } @@ -301,7 +301,7 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err } for { n, err := syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0) - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitWrite(fd.isFile); err == nil { continue } @@ -330,8 +330,10 @@ func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { } switch err { case syscall.EAGAIN: - if err = fd.pd.waitRead(fd.isFile); err == nil { - continue + if fd.pd.pollable() { + if err = fd.pd.waitRead(fd.isFile); err == nil { + continue + } } case syscall.ECONNABORTED: // This means that a socket on the listen @@ -364,7 +366,7 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) { n, err := syscall.ReadDirent(fd.Sysfd, buf) if err != nil { n = 0 - if err == syscall.EAGAIN { + if err == syscall.EAGAIN && fd.pd.pollable() { if err = fd.pd.waitRead(fd.isFile); err == nil { continue } diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go index 7c4cbb17ae..9d79d84575 100644 --- a/src/os/pipe_test.go +++ b/src/os/pipe_test.go @@ -186,3 +186,37 @@ func TestClosedPipeRaceRead(t *testing.T) { func TestClosedPipeRaceWrite(t *testing.T) { testClosedPipeRace(t, false) } + +// Issue 20915: Reading on nonblocking fd should not return "waiting +// 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) { + if os.Getenv("GO_WANT_READ_NONBLOCKING_FD") == "1" { + fd := int(os.Stdin.Fd()) + syscall.SetNonblock(fd, true) + defer syscall.SetNonblock(fd, false) + _, err := os.Stdin.Read(make([]byte, 1)) + if err != nil { + if perr, ok := err.(*os.PathError); !ok || perr.Err != syscall.EAGAIN { + t.Fatalf("read on nonblocking stdin got %q, should have gotten EAGAIN", err) + } + } + os.Exit(0) + } + + testenv.MustHaveExec(t) + r, w, err := os.Pipe() + if err != nil { + t.Fatal(err) + } + defer r.Close() + defer w.Close() + cmd := osexec.Command(os.Args[0], "-test.run="+t.Name()) + cmd.Env = append(os.Environ(), "GO_WANT_READ_NONBLOCKING_FD=1") + cmd.Stdin = r + output, err := cmd.CombinedOutput() + t.Logf("%s", output) + if err != nil { + t.Errorf("child process failed: %v", err) + } +} |