mirror of
https://github.com/golang/go
synced 2024-10-05 06:21:24 -06:00
fa3e4fc429
It is common to close network connection while another goroutine is blocked reading on another goroutine. This sequence corresponds to windows calls to WSARecv to start io, followed by GetQueuedCompletionStatus that blocks until io completes, and, finally, closesocket called from another thread. We were expecting that closesocket would unblock GetQueuedCompletionStatus, and it does, but not always (http://code.google.com/p/go/issues/detail?id=4170#c5). Also that sequence results in connection is being reset. This CL inserts CancelIo between GetQueuedCompletionStatus and closesocket, and waits for both WSARecv and GetQueuedCompletionStatus to complete before proceeding to closesocket. This seems to fix both connection resets and issue 4170. It also makes windows code behave similar to unix version. Unfortunately, CancelIo needs to be called on the same thread as WSARecv. So we have to employ strategy we use for connections with deadlines to every connection now. It means, there are 2 unavoidable thread switches for every io. Some newer versions of windows have new CancelIoEx api that doesn't have these drawbacks, and this CL uses this capability when available. As time goes by, we should have less of CancelIo and more of CancelIoEx systems. Computers with CancelIoEx are also not affected by issue 4195 anymore. Fixes #3710 Fixes #3746 Fixes #4170 Partial fix for issue 4195 R=golang-dev, mikioh.mikioh, bradfitz, rsc CC=golang-dev https://golang.org/cl/6604072
71 lines
1.4 KiB
Go
71 lines
1.4 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package net
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"syscall"
|
|
)
|
|
|
|
type sendfileOp struct {
|
|
anOp
|
|
src syscall.Handle // source
|
|
n uint32
|
|
}
|
|
|
|
func (o *sendfileOp) Submit() (err error) {
|
|
return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND)
|
|
}
|
|
|
|
func (o *sendfileOp) Name() string {
|
|
return "TransmitFile"
|
|
}
|
|
|
|
// sendFile copies the contents of r to c using the TransmitFile
|
|
// system call to minimize copies.
|
|
//
|
|
// if handled == true, sendFile returns the number of bytes copied and any
|
|
// non-EOF error.
|
|
//
|
|
// if handled == false, sendFile performed no work.
|
|
//
|
|
// Note that sendfile for windows does not suppport >2GB file.
|
|
func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
|
|
var n int64 = 0 // by default, copy until EOF
|
|
|
|
lr, ok := r.(*io.LimitedReader)
|
|
if ok {
|
|
n, r = lr.N, lr.R
|
|
if n <= 0 {
|
|
return 0, nil, true
|
|
}
|
|
}
|
|
f, ok := r.(*os.File)
|
|
if !ok {
|
|
return 0, nil, false
|
|
}
|
|
|
|
if err := c.incref(false); err != nil {
|
|
return 0, err, true
|
|
}
|
|
defer c.decref()
|
|
c.wio.Lock()
|
|
defer c.wio.Unlock()
|
|
|
|
var o sendfileOp
|
|
o.Init(c, 'w')
|
|
o.n = uint32(n)
|
|
o.src = syscall.Handle(f.Fd())
|
|
done, err := iosrv.ExecIO(&o, 0)
|
|
if err != nil {
|
|
return 0, err, false
|
|
}
|
|
if lr != nil {
|
|
lr.N -= int64(done)
|
|
}
|
|
return int64(done), nil, true
|
|
}
|