-
Notifications
You must be signed in to change notification settings - Fork 17.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net: io.CopyN fails to copy from file to net.Conn on Windows #25722
Comments
The code path used in this case (copying from a file to a network socket) uses Windows' TransmitFile function. As this might turn out to be a bug in the TransmitFile implementation, it might be relevant that this occurs on Windows 7 Pro Service Pack 1. |
I could reproduce the issue even in C++. Here's poll.SendFile with a fix: // SendFile wraps the TransmitFile call.
func SendFile(fd *FD, src syscall.Handle, n int64) (int64, error) {
ft, err := syscall.GetFileType(src)
if err != nil {
return 0, err
}
// TransmitFile does not work with pipes
if ft == syscall.FILE_TYPE_PIPE {
return 0, syscall.ESPIPE
}
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
o := &fd.wop
o.qty = uint32(n)
o.handle = src
var done int
if n != 0 {
done, err = transmitFileAndFixFilePointer(o)
} else {
done, err = transmitFile(o)
}
return int64(done), err
}
func transmitFile(o *operation) (int, error) {
return wsrv.ExecIO(o, func(o *operation) error {
return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
})
}
// transmitFileAndFixFilePointer calls TransmitFile and, if it failed to advance the file pointer, advances the file pointer
func transmitFileAndFixFilePointer(o *operation) (int, error) {
filePointerBefore, err := getFilePointer(o.handle)
if err != nil {
return 0, err
}
done, err := wsrv.ExecIO(o, func(o *operation) error {
return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
})
if err != nil {
return done, err
}
filePointerAfter, err := getFilePointer(o.handle)
if err != nil {
return done, err
}
if filePointerAfter != filePointerBefore+int64(o.qty) {
_, err = syscall.SetFilePointer(o.handle, int32(o.qty), nil, syscall.FILE_CURRENT)
}
return done, err
}
func getFilePointer(h syscall.Handle) (int64, error) {
var highOffset int32
lowOffset, err := syscall.SetFilePointer(h, 0, &highOffset, syscall.FILE_CURRENT)
if err != nil {
return 0, err
}
return int64(highOffset)<<32 | int64(lowOffset), nil
} This makes three syscalls instead of one and I'm not sure how it performs compared to not using TransmitFile at all. |
I could not reproduce your problem on my Windows 10.
Why do you think Go implementation needs a fix? What is wrong with Go implementation? Alex |
I could reproduce the issue in C++ on my Windows 10 1803. Both samples on the stackoverflow fix the problem. Looks like a bug in |
It uses TransmitFile in a way that doesn't work. It expects TransmitFile to advance the file pointer, which works only for the first call, not for subsequent calls. This happens at least on some systems. While it seems to work on your Windows 10 system, the issue has been observed so far by @creker on Windows 10 1803, by Stack Overflow user Remy Lebeau on Windows 7 Home Premium 64bit and by me on Windows 7 Professional 64 bit. |
Same issue on Windows Server 2016 1607 |
Change https://golang.org/cl/117655 mentions this issue: |
@AndreKR and @creker I just modified our existing test https://go-review.googlesource.com/c/go/+/117655 to hopefully tickle this bug. Our try builder did not complain about my changes (I do not know what Windows version it runs). I suspect my changed test will fail on your computers. Can you, please, verify. I did read https://msdn.microsoft.com/en-us/library/windows/desktop/ms740565(v=vs.85).aspx and it does says:
Perhaps when we wrote this code, we assumed that clearing lpOverlapped will send data starting from current file position (and it does do this for me). But it does not explicitly say that. Perhaps "You can use" confused me. Anyway, if my CL 117655 is broken for you, maybe we could use Windows SetFilePointer to get current file position and copy it into lpOverlapped before calling TransmitFile. Regardless I will need your help, because we don't have computer where I can verify my changes. Thank you. Alex |
@alexbrainman I think your change doesn't trigger the bug. At least the part about advancing file position. Simple test would be to call |
Did you actually try it on your computer?
I modified my test as you suggested - see patch set 2 of my CL https://go-review.googlesource.com/c/go/+/117655/2 As you can see Windows builder is still OK. Does patch 2 fails on your computer? Surprisingly freebsd-amd64 builder failed with exact message we are after: --- FAIL: TestSendfile (0.00s) Alex |
@alexbrainman no, I read the code first and saw that it doesn't match the issue and uses But now that even this change doesn't trigger the bug I will run it and report the results. Will also try previous test just in case. |
Windows 10 1803 Patch set 2 fails
Patch set 1 is ok |
Change https://golang.org/cl/117775 mentions this issue: |
@creker I can reproduce this on one of my Windows 10 computers. So I sent a proper test now - I plan to submit it. Thank you very much. Alex |
Add test for freebsd issue #25809. This test also fails on my Windows 10 Version 1803. My hope is that adding new test will break one of our builders. Updates #25722 Updates #25809 Change-Id: Ia103bc708b8fa3b9af57613acc44893f90b3fa18 Reviewed-on: https://go-review.googlesource.com/117775 Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
Change https://golang.org/cl/117816 mentions this issue: |
@AndreKR and @creker I submitted with new test that fails on my Windows 10, but none of our 5 builders https://build.golang.org fails the test. Please, try new test on current tip and make sure that it fails on your system. I also sent a fix for this issue in Please, also try CL 117816 to see if it fixes new test on your system, and fixes whatever other problems re this issue. I will wait before submitting CL 117816. Thank you. Alex |
I can confirm that the current master (40fc4bb), which includes CL 117775 fails on my machine with:
I can also confirm that the change from CL 117816 fixes the problem. By the way, I just realized that this sentence from the TransmitFile documentation:
is irrelevant in our case, because we are never passing a NULL pointer. Instead, there seems to be another bug in TransmitFile that causes it to start reading at position 3 when |
Same on my Windows 10 1803
That's starting to sound to me like we don't understand something. Would be cool if someone from MS could comment on that. But if it's a bug they probably wouldn't be able to fix it without breaking backwards compatibility. |
Sounds good to me.
SGTM. I will wait for CL 117816 to be reviewed and submitted. Thank you. Alex |
https://go-review.googlesource.com/c/go/+/130855 broke test that is guarding fix for this current issue.
And I think this current issue is broken again. So I am reopening it. I also suspect that TestSendfileParts will fail pretty much on every recent version of Windows 10. (@mattn does it fail for you? Thank you) Maybe we should skip TestSendfileParts and copy that change to release-branch.go1.11 ? /cc @bradfitz Alex |
The test does three instances of "copy 3 bytes." It expects to see the first nine characters, which are "Produced ". In the failing test case it is instead getting "Producduc". It's clear that it is getting characters 0-3, then characters 4-6, then characters 4-6 again. Each "copy 3 bytes" action is done by calling My guess is that in some cases |
Basically, try changing the end of internal/poll/sendfile_windows.go to look like this: done, err := wsrv.ExecIO(o, func(o *operation) error {
return syscall.TransmitFile(o.fd.Sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
})
if err == nil {
_, err = syscall.Seek(o.handle, int64(done), 1)
}
return int64(done), err |
Sure:
Alex |
Oh yeah, I forgot that it appears that on some systems and in some cases the file position is updated, and in others it is not. I do not understand the logic of what is happening. Try instead something like |
This works (thank you):
I will send a patch when free. Unless someone beats me to it. Alex |
Nice to hear that works. |
Change https://golang.org/cl/131976 mentions this issue: |
Sad we do not have Windows 10 builder that would catch errors like that. @mattn please try https://go-review.googlesource.com/c/go/+/131976 to see if it fixes broken net.TestSendfileParts on your Windows 10. Thank you. Alex |
Should we push CL 131976 to release-branch.go1.11 branch? Alex |
@gopherbot please consider backporting this issue to Go 1.11 |
Backport issue(s) opened: #27411 (for 1.11). Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases. |
@gopherbot please consider back porting this issue to Go 1.10. It's broken on Windows 10 and workarounds are difficult if the CopyN call happens deep into some networking library. |
Some versions of Windows (Windows 10 1803) do not set file position after TransmitFile completes. So just use Seek to set file position before returning from sendfile. Fixes golang#25722 Change-Id: I7a49be10304b5db19dda707b13ac93d338aeb190 Reviewed-on: https://go-review.googlesource.com/131976 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Yasuhiro MATSUMOTO <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Add test for freebsd issue golang#25809. This test also fails on my Windows 10 Version 1803. My hope is that adding new test will break one of our builders. Updates golang#25722 Updates golang#25809 Change-Id: Ia103bc708b8fa3b9af57613acc44893f90b3fa18 Reviewed-on: https://go-review.googlesource.com/117775 Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
Current SendFile implementation assumes that TransmitFile starts from the current file position. But that appears not true for Windows 10 Version 1803. TransmitFile documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms740565(v=vs.85).aspx suggests, "You can use the lpOverlapped parameter to specify a 64-bit offset within the file at which to start the file data transfer by setting the Offset and OffsetHigh member of the OVERLAPPED structure." Do as it advises. Fixes golang#25722 Change-Id: I241d3bf76d0d5590d4df27c6f922d637068232fb Reviewed-on: https://go-review.googlesource.com/117816 Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
Some versions of Windows (Windows 10 1803) do not set file position after TransmitFile completes. So just use Seek to set file position before returning from sendfile. Fixes golang#25722 Change-Id: I7a49be10304b5db19dda707b13ac93d338aeb190 Reviewed-on: https://go-review.googlesource.com/131976 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Yasuhiro MATSUMOTO <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Some versions of Windows (Windows 10 1803) do not set file position after TransmitFile completes. So just use Seek to set file position before returning from sendfile. Fixes golang#25722 Change-Id: I7a49be10304b5db19dda707b13ac93d338aeb190 Reviewed-on: https://go-review.googlesource.com/131976 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Yasuhiro MATSUMOTO <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
Change https://golang.org/cl/146779 mentions this issue: |
Change https://golang.org/cl/146780 mentions this issue: |
…when calling TransmitFile Current SendFile implementation assumes that TransmitFile starts from the current file position. But that appears not true for Windows 10 Version 1803. TransmitFile documentation https://msdn.microsoft.com/en-us/library/windows/desktop/ms740565(v=vs.85).aspx suggests, "You can use the lpOverlapped parameter to specify a 64-bit offset within the file at which to start the file data transfer by setting the Offset and OffsetHigh member of the OVERLAPPED structure." Do as it advises. Fixes #25722 Change-Id: I241d3bf76d0d5590d4df27c6f922d637068232fb Reviewed-on: https://go-review.googlesource.com/117816 Run-TryBot: Alex Brainman <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> (cherry picked from commit af4d604) Reviewed-on: https://go-review.googlesource.com/c/146780 Run-TryBot: Ian Lance Taylor <[email protected]>
…ws sendfile Some versions of Windows (Windows 10 1803) do not set file position after TransmitFile completes. So just use Seek to set file position before returning from sendfile. Updates #25722 Fixes #27419 Change-Id: I7a49be10304b5db19dda707b13ac93d338aeb190 Reviewed-on: https://go-review.googlesource.com/131976 Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Yasuhiro MATSUMOTO <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> (cherry picked from commit 8359b5e) Reviewed-on: https://go-review.googlesource.com/c/146779 Run-TryBot: Ian Lance Taylor <[email protected]>
What version of Go are you using (
go version
)?go version go1.10.2 windows/amd64
Does this issue reproduce with the latest release?
That is the latest (stable) release.
What operating system and processor architecture are you using (
go env
)?windows/amd64
What did you do?
alphabet.txt
:What did you expect to see?
What did you see instead?
Notes
I could only reproduce this when copying from a file to a network connection. If I for example replace the network connection with
os.Stdout
or the file withstrings.NewReader()
the issue doesn't happen.The text was updated successfully, but these errors were encountered: