mirror of
https://github.com/golang/go
synced 2024-11-20 07:54:39 -07:00
net: refactor windows code
R=golang-dev, rsc CC=golang-dev https://golang.org/cl/4185054
This commit is contained in:
parent
0cf6f8c096
commit
0e7995c02f
@ -13,11 +13,192 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type InvalidConnError struct{}
|
||||
|
||||
func (e *InvalidConnError) String() string { return "invalid net.Conn" }
|
||||
func (e *InvalidConnError) Temporary() bool { return false }
|
||||
func (e *InvalidConnError) Timeout() bool { return false }
|
||||
|
||||
var initErr os.Error
|
||||
|
||||
func init() {
|
||||
var d syscall.WSAData
|
||||
e := syscall.WSAStartup(uint32(0x101), &d)
|
||||
if e != 0 {
|
||||
initErr = os.NewSyscallError("WSAStartup", e)
|
||||
}
|
||||
}
|
||||
|
||||
func closesocket(s int) (errno int) {
|
||||
return syscall.Closesocket(int32(s))
|
||||
}
|
||||
|
||||
// Interface for all io operations.
|
||||
type anOpIface interface {
|
||||
Op() *anOp
|
||||
Name() string
|
||||
Submit() (errno int)
|
||||
}
|
||||
|
||||
// IO completion result parameters.
|
||||
type ioResult struct {
|
||||
key uint32
|
||||
qty uint32
|
||||
errno int
|
||||
qty uint32
|
||||
err int
|
||||
}
|
||||
|
||||
// anOp implements functionality common to all io operations.
|
||||
type anOp struct {
|
||||
// Used by IOCP interface, it must be first field
|
||||
// of the struct, as our code rely on it.
|
||||
o syscall.Overlapped
|
||||
|
||||
resultc chan ioResult // io completion results
|
||||
errnoc chan int // io submit / cancel operation errors
|
||||
fd *netFD
|
||||
}
|
||||
|
||||
func (o *anOp) Init(fd *netFD) {
|
||||
o.fd = fd
|
||||
o.resultc = make(chan ioResult, 1)
|
||||
o.errnoc = make(chan int)
|
||||
}
|
||||
|
||||
func (o *anOp) Op() *anOp {
|
||||
return o
|
||||
}
|
||||
|
||||
// bufOp is used by io operations that read / write
|
||||
// data from / to client buffer.
|
||||
type bufOp struct {
|
||||
anOp
|
||||
buf syscall.WSABuf
|
||||
}
|
||||
|
||||
func (o *bufOp) Init(fd *netFD, buf []byte) {
|
||||
o.anOp.Init(fd)
|
||||
o.buf.Len = uint32(len(buf))
|
||||
if len(buf) == 0 {
|
||||
o.buf.Buf = nil
|
||||
} else {
|
||||
o.buf.Buf = (*byte)(unsafe.Pointer(&buf[0]))
|
||||
}
|
||||
}
|
||||
|
||||
// resultSrv will retreive all io completion results from
|
||||
// iocp and send them to the correspondent waiting client
|
||||
// goroutine via channel supplied in the request.
|
||||
type resultSrv struct {
|
||||
iocp int32
|
||||
}
|
||||
|
||||
func (s *resultSrv) Run() {
|
||||
var o *syscall.Overlapped
|
||||
var key uint32
|
||||
var r ioResult
|
||||
for {
|
||||
r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
|
||||
switch {
|
||||
case r.err == 0:
|
||||
// Dequeued successfully completed io packet.
|
||||
case r.err == syscall.WAIT_TIMEOUT && o == nil:
|
||||
// Wait has timed out (should not happen now, but might be used in the future).
|
||||
panic("GetQueuedCompletionStatus timed out")
|
||||
case o == nil:
|
||||
// Failed to dequeue anything -> report the error.
|
||||
panic("GetQueuedCompletionStatus failed " + syscall.Errstr(r.err))
|
||||
default:
|
||||
// Dequeued failed io packet.
|
||||
}
|
||||
(*anOp)(unsafe.Pointer(o)).resultc <- r
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ioSrv executes net io requests.
|
||||
type ioSrv struct {
|
||||
submchan chan anOpIface // submit io requests
|
||||
canchan chan anOpIface // cancel io requests
|
||||
}
|
||||
|
||||
// ProcessRemoteIO will execute submit io requests on behalf
|
||||
// of other goroutines, all on a single os thread, so it can
|
||||
// cancel them later. Results of all operations will be sent
|
||||
// back to their requesters via channel supplied in request.
|
||||
func (s *ioSrv) ProcessRemoteIO() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
for {
|
||||
select {
|
||||
case o := <-s.submchan:
|
||||
o.Op().errnoc <- o.Submit()
|
||||
case o := <-s.canchan:
|
||||
o.Op().errnoc <- syscall.CancelIo(uint32(o.Op().fd.sysfd))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExecIO executes a single io operation. It either executes it
|
||||
// inline, or, if timeouts are employed, passes the request onto
|
||||
// a special goroutine and waits for completion or cancels request.
|
||||
func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err os.Error) {
|
||||
var e int
|
||||
o := oi.Op()
|
||||
if deadline_delta > 0 {
|
||||
// Send request to a special dedicated thread,
|
||||
// so it can stop the io with CancelIO later.
|
||||
s.submchan <- oi
|
||||
e = <-o.errnoc
|
||||
} else {
|
||||
e = oi.Submit()
|
||||
}
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(e)}
|
||||
}
|
||||
// Wait for our request to complete.
|
||||
var r ioResult
|
||||
if deadline_delta > 0 {
|
||||
select {
|
||||
case r = <-o.resultc:
|
||||
case <-time.After(deadline_delta):
|
||||
s.canchan <- oi
|
||||
<-o.errnoc
|
||||
r = <-o.resultc
|
||||
if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
|
||||
r.err = syscall.EWOULDBLOCK
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r = <-o.resultc
|
||||
}
|
||||
if r.err != 0 {
|
||||
err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, os.Errno(r.err)}
|
||||
}
|
||||
return int(r.qty), err
|
||||
}
|
||||
|
||||
// Start helper goroutines.
|
||||
var resultsrv *resultSrv
|
||||
var iosrv *ioSrv
|
||||
var onceStartServer sync.Once
|
||||
|
||||
func startServer() {
|
||||
resultsrv = new(resultSrv)
|
||||
var errno int
|
||||
resultsrv.iocp, errno = syscall.CreateIoCompletionPort(-1, 0, 0, 1)
|
||||
if errno != 0 {
|
||||
panic("CreateIoCompletionPort failed " + syscall.Errstr(errno))
|
||||
}
|
||||
go resultsrv.Run()
|
||||
|
||||
iosrv = new(ioSrv)
|
||||
iosrv.submchan = make(chan anOpIface)
|
||||
iosrv.canchan = make(chan anOpIface)
|
||||
go iosrv.ProcessRemoteIO()
|
||||
}
|
||||
|
||||
// Network file descriptor.
|
||||
@ -31,8 +212,6 @@ type netFD struct {
|
||||
sysfd int
|
||||
family int
|
||||
proto int
|
||||
cr chan *ioResult
|
||||
cw chan *ioResult
|
||||
net string
|
||||
laddr Addr
|
||||
raddr Addr
|
||||
@ -46,114 +225,29 @@ type netFD struct {
|
||||
wio sync.Mutex
|
||||
}
|
||||
|
||||
type InvalidConnError struct{}
|
||||
|
||||
func (e *InvalidConnError) String() string { return "invalid net.Conn" }
|
||||
func (e *InvalidConnError) Temporary() bool { return false }
|
||||
func (e *InvalidConnError) Timeout() bool { return false }
|
||||
|
||||
// pollServer will run around waiting for io completion request
|
||||
// to arrive. Every request received will contain channel to signal
|
||||
// io owner about the completion.
|
||||
|
||||
type pollServer struct {
|
||||
iocp int32
|
||||
}
|
||||
|
||||
func newPollServer() (s *pollServer, err os.Error) {
|
||||
s = new(pollServer)
|
||||
var e int
|
||||
if s.iocp, e = syscall.CreateIoCompletionPort(-1, 0, 0, 1); e != 0 {
|
||||
return nil, os.NewSyscallError("CreateIoCompletionPort", e)
|
||||
func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) {
|
||||
f = &netFD{
|
||||
sysfd: fd,
|
||||
family: family,
|
||||
proto: proto,
|
||||
net: net,
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
}
|
||||
go s.Run()
|
||||
return s, nil
|
||||
runtime.SetFinalizer(f, (*netFD).Close)
|
||||
return f
|
||||
}
|
||||
|
||||
type ioPacket struct {
|
||||
// Used by IOCP interface,
|
||||
// it must be first field of the struct,
|
||||
// as our code rely on it.
|
||||
o syscall.Overlapped
|
||||
|
||||
// Link to the io owner.
|
||||
c chan *ioResult
|
||||
|
||||
w *syscall.WSABuf
|
||||
}
|
||||
|
||||
func (s *pollServer) getCompletedIO() (ov *syscall.Overlapped, result *ioResult, err os.Error) {
|
||||
var r ioResult
|
||||
var o *syscall.Overlapped
|
||||
e := syscall.GetQueuedCompletionStatus(s.iocp, &r.qty, &r.key, &o, syscall.INFINITE)
|
||||
switch {
|
||||
case e == 0:
|
||||
// Dequeued successfully completed io packet.
|
||||
return o, &r, nil
|
||||
case e == syscall.WAIT_TIMEOUT && o == nil:
|
||||
// Wait has timed out (should not happen now, but might be used in the future).
|
||||
return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e)
|
||||
case o == nil:
|
||||
// Failed to dequeue anything -> report the error.
|
||||
return nil, &r, os.NewSyscallError("GetQueuedCompletionStatus", e)
|
||||
default:
|
||||
// Dequeued failed io packet.
|
||||
r.errno = e
|
||||
return o, &r, nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *pollServer) Run() {
|
||||
for {
|
||||
o, r, err := s.getCompletedIO()
|
||||
if err != nil {
|
||||
panic("Run pollServer: " + err.String() + "\n")
|
||||
}
|
||||
p := (*ioPacket)(unsafe.Pointer(o))
|
||||
p.c <- r
|
||||
}
|
||||
}
|
||||
|
||||
// Network FD methods.
|
||||
// All the network FDs use a single pollServer.
|
||||
|
||||
var pollserver *pollServer
|
||||
var onceStartServer sync.Once
|
||||
|
||||
func startServer() {
|
||||
p, err := newPollServer()
|
||||
if err != nil {
|
||||
panic("Start pollServer: " + err.String() + "\n")
|
||||
}
|
||||
pollserver = p
|
||||
|
||||
go timeoutIO()
|
||||
}
|
||||
|
||||
var initErr os.Error
|
||||
|
||||
func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
|
||||
if initErr != nil {
|
||||
return nil, initErr
|
||||
}
|
||||
onceStartServer.Do(startServer)
|
||||
// Associate our socket with pollserver.iocp.
|
||||
if _, e := syscall.CreateIoCompletionPort(int32(fd), pollserver.iocp, 0, 0); e != 0 {
|
||||
// Associate our socket with resultsrv.iocp.
|
||||
if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
|
||||
return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)}
|
||||
}
|
||||
f = &netFD{
|
||||
sysfd: fd,
|
||||
family: family,
|
||||
proto: proto,
|
||||
cr: make(chan *ioResult, 1),
|
||||
cw: make(chan *ioResult, 1),
|
||||
net: net,
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
}
|
||||
runtime.SetFinalizer(f, (*netFD).Close)
|
||||
return f, nil
|
||||
return allocFD(fd, family, proto, net, laddr, raddr), nil
|
||||
}
|
||||
|
||||
// Add a reference to this fd.
|
||||
@ -172,7 +266,7 @@ func (fd *netFD) decref() {
|
||||
// In case the user has set linger, switch to blocking mode so
|
||||
// the close blocks. As long as this doesn't happen often, we
|
||||
// can handle the extra OS processes. Otherwise we'll need to
|
||||
// use the pollserver for Close too. Sigh.
|
||||
// use the resultsrv for Close too. Sigh.
|
||||
syscall.SetNonblock(fd.sysfd, false)
|
||||
closesocket(fd.sysfd)
|
||||
fd.sysfd = -1
|
||||
@ -194,89 +288,22 @@ func (fd *netFD) Close() os.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newWSABuf(p []byte) *syscall.WSABuf {
|
||||
var p0 *byte
|
||||
if len(p) > 0 {
|
||||
p0 = (*byte)(unsafe.Pointer(&p[0]))
|
||||
}
|
||||
return &syscall.WSABuf{uint32(len(p)), p0}
|
||||
// Read from network.
|
||||
|
||||
type readOp struct {
|
||||
bufOp
|
||||
}
|
||||
|
||||
func waitPacket(fd *netFD, pckt *ioPacket, mode int) (r *ioResult) {
|
||||
var delta int64
|
||||
if mode == 'r' {
|
||||
delta = fd.rdeadline_delta
|
||||
}
|
||||
if mode == 'w' {
|
||||
delta = fd.wdeadline_delta
|
||||
}
|
||||
if delta <= 0 {
|
||||
return <-pckt.c
|
||||
}
|
||||
|
||||
select {
|
||||
case r = <-pckt.c:
|
||||
case <-time.After(delta):
|
||||
a := &arg{f: cancel, fd: fd, pckt: pckt, c: make(chan int)}
|
||||
ioChan <- a
|
||||
<-a.c
|
||||
r = <-pckt.c
|
||||
if r.errno == 995 { // IO Canceled
|
||||
r.errno = syscall.EWOULDBLOCK
|
||||
}
|
||||
}
|
||||
return r
|
||||
func (o *readOp) Submit() (errno int) {
|
||||
var d, f uint32
|
||||
return syscall.WSARecv(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
|
||||
}
|
||||
|
||||
const (
|
||||
read = iota
|
||||
readfrom
|
||||
write
|
||||
writeto
|
||||
cancel
|
||||
)
|
||||
|
||||
type arg struct {
|
||||
f int
|
||||
fd *netFD
|
||||
pckt *ioPacket
|
||||
done *uint32
|
||||
flags *uint32
|
||||
rsa *syscall.RawSockaddrAny
|
||||
size *int32
|
||||
sa *syscall.Sockaddr
|
||||
c chan int
|
||||
func (o *readOp) Name() string {
|
||||
return "WSARecv"
|
||||
}
|
||||
|
||||
var ioChan chan *arg = make(chan *arg)
|
||||
|
||||
func timeoutIO() {
|
||||
// CancelIO only cancels all pending input and output (I/O) operations that are
|
||||
// issued by the calling thread for the specified file, does not cancel I/O
|
||||
// operations that other threads issue for a file handle. So we need do all timeout
|
||||
// I/O in single OS thread.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
for {
|
||||
o := <-ioChan
|
||||
var e int
|
||||
switch o.f {
|
||||
case read:
|
||||
e = syscall.WSARecv(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, &o.pckt.o, nil)
|
||||
case readfrom:
|
||||
e = syscall.WSARecvFrom(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, o.flags, o.rsa, o.size, &o.pckt.o, nil)
|
||||
case write:
|
||||
e = syscall.WSASend(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, uint32(0), &o.pckt.o, nil)
|
||||
case writeto:
|
||||
e = syscall.WSASendto(uint32(o.fd.sysfd), o.pckt.w, 1, o.done, 0, *o.sa, &o.pckt.o, nil)
|
||||
case cancel:
|
||||
e = syscall.CancelIo(uint32(o.fd.sysfd))
|
||||
}
|
||||
o.c <- e
|
||||
}
|
||||
}
|
||||
|
||||
func (fd *netFD) Read(p []byte) (n int, err os.Error) {
|
||||
func (fd *netFD) Read(buf []byte) (n int, err os.Error) {
|
||||
if fd == nil {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
@ -287,45 +314,37 @@ func (fd *netFD) Read(p []byte) (n int, err os.Error) {
|
||||
if fd.sysfd == -1 {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
// Submit receive request.
|
||||
var pckt ioPacket
|
||||
pckt.c = fd.cr
|
||||
pckt.w = newWSABuf(p)
|
||||
var done uint32
|
||||
flags := uint32(0)
|
||||
var e int
|
||||
if fd.rdeadline_delta > 0 {
|
||||
a := &arg{f: read, fd: fd, pckt: &pckt, done: &done, flags: &flags, c: make(chan int)}
|
||||
ioChan <- a
|
||||
e = <-a.c
|
||||
} else {
|
||||
e = syscall.WSARecv(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &pckt.o, nil)
|
||||
}
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
return 0, &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
// Wait for our request to complete.
|
||||
r := waitPacket(fd, &pckt, 'r')
|
||||
if r.errno != 0 {
|
||||
err = &OpError{"WSARecv", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
}
|
||||
n = int(r.qty)
|
||||
var o readOp
|
||||
o.Init(fd, buf)
|
||||
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
|
||||
if err == nil && n == 0 {
|
||||
err = os.EOF
|
||||
err = &OpError{o.Name(), o.fd.net, o.fd.laddr, os.EOF}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) {
|
||||
// ReadFrom from network.
|
||||
|
||||
type readFromOp struct {
|
||||
bufOp
|
||||
rsa syscall.RawSockaddrAny
|
||||
}
|
||||
|
||||
func (o *readFromOp) Submit() (errno int) {
|
||||
var d, f uint32
|
||||
l := int32(unsafe.Sizeof(o.rsa))
|
||||
return syscall.WSARecvFrom(uint32(o.fd.sysfd), &o.buf, 1, &d, &f, &o.rsa, &l, &o.o, nil)
|
||||
}
|
||||
|
||||
func (o *readFromOp) Name() string {
|
||||
return "WSARecvFrom"
|
||||
}
|
||||
|
||||
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err os.Error) {
|
||||
if fd == nil {
|
||||
return 0, nil, os.EINVAL
|
||||
}
|
||||
if len(p) == 0 {
|
||||
if len(buf) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
fd.rio.Lock()
|
||||
@ -335,41 +354,29 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err os.Error) {
|
||||
if fd.sysfd == -1 {
|
||||
return 0, nil, os.EINVAL
|
||||
}
|
||||
// Submit receive request.
|
||||
var pckt ioPacket
|
||||
pckt.c = fd.cr
|
||||
pckt.w = newWSABuf(p)
|
||||
var done uint32
|
||||
flags := uint32(0)
|
||||
var rsa syscall.RawSockaddrAny
|
||||
l := int32(unsafe.Sizeof(rsa))
|
||||
var e int
|
||||
if fd.rdeadline_delta > 0 {
|
||||
a := &arg{f: readfrom, fd: fd, pckt: &pckt, done: &done, flags: &flags, rsa: &rsa, size: &l, c: make(chan int)}
|
||||
ioChan <- a
|
||||
e = <-a.c
|
||||
} else {
|
||||
e = syscall.WSARecvFrom(uint32(fd.sysfd), pckt.w, 1, &done, &flags, &rsa, &l, &pckt.o, nil)
|
||||
}
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
return 0, nil, &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
// Wait for our request to complete.
|
||||
r := waitPacket(fd, &pckt, 'r')
|
||||
if r.errno != 0 {
|
||||
err = &OpError{"WSARecvFrom", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
}
|
||||
n = int(r.qty)
|
||||
sa, _ = rsa.Sockaddr()
|
||||
var o readFromOp
|
||||
o.Init(fd, buf)
|
||||
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
|
||||
sa, _ = o.rsa.Sockaddr()
|
||||
return
|
||||
}
|
||||
|
||||
func (fd *netFD) Write(p []byte) (n int, err os.Error) {
|
||||
// Write to network.
|
||||
|
||||
type writeOp struct {
|
||||
bufOp
|
||||
}
|
||||
|
||||
func (o *writeOp) Submit() (errno int) {
|
||||
var d uint32
|
||||
return syscall.WSASend(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, &o.o, nil)
|
||||
}
|
||||
|
||||
func (o *writeOp) Name() string {
|
||||
return "WSASend"
|
||||
}
|
||||
|
||||
func (fd *netFD) Write(buf []byte) (n int, err os.Error) {
|
||||
if fd == nil {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
@ -380,41 +387,32 @@ func (fd *netFD) Write(p []byte) (n int, err os.Error) {
|
||||
if fd.sysfd == -1 {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
// Submit send request.
|
||||
var pckt ioPacket
|
||||
pckt.c = fd.cw
|
||||
pckt.w = newWSABuf(p)
|
||||
var done uint32
|
||||
var e int
|
||||
if fd.wdeadline_delta > 0 {
|
||||
a := &arg{f: write, fd: fd, pckt: &pckt, done: &done, c: make(chan int)}
|
||||
ioChan <- a
|
||||
e = <-a.c
|
||||
} else {
|
||||
e = syscall.WSASend(uint32(fd.sysfd), pckt.w, 1, &done, uint32(0), &pckt.o, nil)
|
||||
}
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
return 0, &OpError{"WSASend", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
// Wait for our request to complete.
|
||||
r := waitPacket(fd, &pckt, 'w')
|
||||
if r.errno != 0 {
|
||||
err = &OpError{"WSASend", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
}
|
||||
n = int(r.qty)
|
||||
return
|
||||
var o writeOp
|
||||
o.Init(fd, buf)
|
||||
return iosrv.ExecIO(&o, fd.wdeadline_delta)
|
||||
}
|
||||
|
||||
func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) {
|
||||
// WriteTo to network.
|
||||
|
||||
type writeToOp struct {
|
||||
bufOp
|
||||
sa syscall.Sockaddr
|
||||
}
|
||||
|
||||
func (o *writeToOp) Submit() (errno int) {
|
||||
var d uint32
|
||||
return syscall.WSASendto(uint32(o.fd.sysfd), &o.buf, 1, &d, 0, o.sa, &o.o, nil)
|
||||
}
|
||||
|
||||
func (o *writeToOp) Name() string {
|
||||
return "WSASendto"
|
||||
}
|
||||
|
||||
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err os.Error) {
|
||||
if fd == nil {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
if len(p) == 0 {
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
fd.wio.Lock()
|
||||
@ -424,34 +422,29 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err os.Error) {
|
||||
if fd.sysfd == -1 {
|
||||
return 0, os.EINVAL
|
||||
}
|
||||
// Submit send request.
|
||||
var pckt ioPacket
|
||||
pckt.c = fd.cw
|
||||
pckt.w = newWSABuf(p)
|
||||
var done uint32
|
||||
var e int
|
||||
if fd.wdeadline_delta > 0 {
|
||||
a := &arg{f: writeto, fd: fd, pckt: &pckt, done: &done, sa: &sa, c: make(chan int)}
|
||||
ioChan <- a
|
||||
e = <-a.c
|
||||
} else {
|
||||
e = syscall.WSASendto(uint32(fd.sysfd), pckt.w, 1, &done, 0, sa, &pckt.o, nil)
|
||||
}
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
return 0, &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
// Wait for our request to complete.
|
||||
r := waitPacket(fd, &pckt, 'w')
|
||||
if r.errno != 0 {
|
||||
err = &OpError{"WSASendTo", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
}
|
||||
n = int(r.qty)
|
||||
return
|
||||
var o writeToOp
|
||||
o.Init(fd, buf)
|
||||
o.sa = sa
|
||||
return iosrv.ExecIO(&o, fd.wdeadline_delta)
|
||||
}
|
||||
|
||||
// Accept new network connections.
|
||||
|
||||
type acceptOp struct {
|
||||
anOp
|
||||
newsock int
|
||||
attrs [2]syscall.RawSockaddrAny // space for local and remote address only
|
||||
}
|
||||
|
||||
func (o *acceptOp) Submit() (errno int) {
|
||||
var d uint32
|
||||
l := uint32(unsafe.Sizeof(o.attrs[0]))
|
||||
return syscall.AcceptEx(uint32(o.fd.sysfd), uint32(o.newsock),
|
||||
(*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o)
|
||||
}
|
||||
|
||||
func (o *acceptOp) Name() string {
|
||||
return "AcceptEx"
|
||||
}
|
||||
|
||||
func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.Error) {
|
||||
@ -474,72 +467,40 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err os.
|
||||
|
||||
// Associate our new socket with IOCP.
|
||||
onceStartServer.Do(startServer)
|
||||
if _, e = syscall.CreateIoCompletionPort(int32(s), pollserver.iocp, 0, 0); e != 0 {
|
||||
if _, e = syscall.CreateIoCompletionPort(int32(s), resultsrv.iocp, 0, 0); e != 0 {
|
||||
return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
|
||||
// Submit accept request.
|
||||
// Will use new unique channel here, because, unlike Read or Write,
|
||||
// Accept is expected to be executed by many goroutines simultaniously.
|
||||
var pckt ioPacket
|
||||
pckt.c = make(chan *ioResult)
|
||||
attrs, e := syscall.AcceptIOCP(fd.sysfd, s, &pckt.o)
|
||||
switch e {
|
||||
case 0:
|
||||
// IO completed immediately, but we need to get our completion message anyway.
|
||||
case syscall.ERROR_IO_PENDING:
|
||||
// IO started, and we have to wait for it's completion.
|
||||
default:
|
||||
var o acceptOp
|
||||
o.Init(fd)
|
||||
o.newsock = s
|
||||
_, err = iosrv.ExecIO(&o, 0)
|
||||
if err != nil {
|
||||
closesocket(s)
|
||||
return nil, &OpError{"AcceptEx", fd.net, fd.laddr, os.Errno(e)}
|
||||
}
|
||||
|
||||
// Wait for peer connection.
|
||||
r := <-pckt.c
|
||||
if r.errno != 0 {
|
||||
closesocket(s)
|
||||
return nil, &OpError{"AcceptEx", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Inherit properties of the listening socket.
|
||||
e = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, fd.sysfd)
|
||||
if e != 0 {
|
||||
closesocket(s)
|
||||
return nil, &OpError{"Setsockopt", fd.net, fd.laddr, os.Errno(r.errno)}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get local and peer addr out of AcceptEx buffer.
|
||||
lsa, rsa := syscall.GetAcceptIOCPSockaddrs(attrs)
|
||||
var lrsa, rrsa *syscall.RawSockaddrAny
|
||||
var llen, rlen int32
|
||||
l := uint32(unsafe.Sizeof(*lrsa))
|
||||
syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&o.attrs[0])),
|
||||
0, l, l, &lrsa, &llen, &rrsa, &rlen)
|
||||
lsa, _ := lrsa.Sockaddr()
|
||||
rsa, _ := rrsa.Sockaddr()
|
||||
|
||||
// Create our netFD and return it for further use.
|
||||
laddr := toAddr(lsa)
|
||||
raddr := toAddr(rsa)
|
||||
|
||||
f := &netFD{
|
||||
sysfd: s,
|
||||
family: fd.family,
|
||||
proto: fd.proto,
|
||||
cr: make(chan *ioResult, 1),
|
||||
cw: make(chan *ioResult, 1),
|
||||
net: fd.net,
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
}
|
||||
runtime.SetFinalizer(f, (*netFD).Close)
|
||||
return f, nil
|
||||
return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil
|
||||
}
|
||||
|
||||
func closesocket(s int) (errno int) {
|
||||
return syscall.Closesocket(int32(s))
|
||||
}
|
||||
|
||||
func init() {
|
||||
var d syscall.WSAData
|
||||
e := syscall.WSAStartup(uint32(0x101), &d)
|
||||
if e != 0 {
|
||||
initErr = os.NewSyscallError("WSAStartup", e)
|
||||
}
|
||||
}
|
||||
// Not implemeted functions.
|
||||
|
||||
func (fd *netFD) dup() (f *os.File, err os.Error) {
|
||||
// TODO: Implement this
|
||||
|
@ -653,26 +653,6 @@ func Shutdown(fd, how int) (errno int) {
|
||||
return int(shutdown(int32(fd), int32(how)))
|
||||
}
|
||||
|
||||
func AcceptIOCP(iocpfd, fd int, o *Overlapped) (attrs *byte, errno int) {
|
||||
// Will ask for local and remote address only.
|
||||
rsa := make([]RawSockaddrAny, 2)
|
||||
attrs = (*byte)(unsafe.Pointer(&rsa[0]))
|
||||
alen := uint32(unsafe.Sizeof(rsa[0]))
|
||||
var done uint32
|
||||
errno = AcceptEx(uint32(iocpfd), uint32(fd), attrs, 0, alen, alen, &done, o)
|
||||
return
|
||||
}
|
||||
|
||||
func GetAcceptIOCPSockaddrs(attrs *byte) (lsa, rsa Sockaddr) {
|
||||
var lrsa, rrsa *RawSockaddrAny
|
||||
var llen, rlen int32
|
||||
alen := uint32(unsafe.Sizeof(*lrsa))
|
||||
GetAcceptExSockaddrs(attrs, 0, alen, alen, &lrsa, &llen, &rrsa, &rlen)
|
||||
lsa, _ = lrsa.Sockaddr()
|
||||
rsa, _ = rrsa.Sockaddr()
|
||||
return
|
||||
}
|
||||
|
||||
func WSASendto(s uint32, bufs *WSABuf, bufcnt uint32, sent *uint32, flags uint32, to Sockaddr, overlapped *Overlapped, croutine *byte) (errno int) {
|
||||
rsa, l, err := to.sockaddr()
|
||||
if err != 0 {
|
||||
|
@ -28,6 +28,7 @@ const (
|
||||
ERROR_PROC_NOT_FOUND = 127
|
||||
ERROR_ENVVAR_NOT_FOUND = 203
|
||||
ERROR_DIRECTORY = 267
|
||||
ERROR_OPERATION_ABORTED = 995
|
||||
ERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user