Skip to content

Commit 3320ce9

Browse files
panjf2000neild
authored andcommitted
internal/poll: handle the special case of sendfile(2) sending the full chunk
CL 622235 would fix #70000 while resulting in one extra sendfile(2) system call when sendfile(2) returns (>0, EAGAIN). That's also why I left sendfile_bsd.go behind, and didn't make it line up with other two implementations: sendfile_linux.go and sendfile_solaris.go. Unlike sendfile(2)'s on Linux and Solaris that always return (0, EAGAIN), sendfile(2)'s on *BSD and macOS may return (>0, EAGAIN) when using a socket marked for non-blocking I/O. In that case, the current code will try to re-call sendfile(2) immediately, which will most likely get us a (0, EAGAIN). After that, it goes to `dstFD.pd.waitWrite(dstFD.isFile)` below, which should have been done in the first place. Thus, the real problem that leads to #70000 is that the old code doesn't handle the special case of sendfile(2) sending the exact number of bytes the caller requested. Fixes #70000 Change-Id: I6073d6b9feb58b3d7e114ec21e4e80d9727bca66 Reviewed-on: https://go-review.googlesource.com/c/go/+/622255 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Run-TryBot: Andy Pan <[email protected]>
1 parent 22664f3 commit 3320ce9

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

src/internal/poll/sendfile_bsd.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,28 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (written int64, err error,
3535
if int64(n) > remain {
3636
n = int(remain)
3737
}
38+
m := n
3839
pos1 := pos
3940
n, err = syscall.Sendfile(dst, src, &pos1, n)
4041
if n > 0 {
4142
pos += int64(n)
4243
written += int64(n)
4344
remain -= int64(n)
44-
continue
45+
// (n, nil) indicates that sendfile(2) has transferred
46+
// the exact number of bytes we requested, or some unretryable
47+
// error have occurred with partial bytes sent. Either way, we
48+
// don't need to go through the following logic to check EINTR
49+
// or fell into dstFD.pd.waitWrite, just continue to send the
50+
// next chunk or break the loop.
51+
if n == m {
52+
continue
53+
} else if err != syscall.EAGAIN &&
54+
err != syscall.EINTR &&
55+
err != syscall.EBUSY {
56+
// Particularly, EPIPE. Errors like that would normally lead
57+
// the subsequent sendfile(2) call to (-1, EBADF).
58+
break
59+
}
4560
} else if err != syscall.EAGAIN && err != syscall.EINTR {
4661
// This includes syscall.ENOSYS (no kernel
4762
// support) and syscall.EINVAL (fd types which

0 commit comments

Comments
 (0)