Commit fa3e4fc4 authored by Alex Brainman's avatar Alex Brainman

net: fix connection resets when closed on windows

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
parent ad487dad
...@@ -261,6 +261,8 @@ var pollMaxN int ...@@ -261,6 +261,8 @@ var pollMaxN int
var pollservers []*pollServer var pollservers []*pollServer
var startServersOnce []func() var startServersOnce []func()
var canCancelIO = true // used for testing current package
func init() { func init() {
pollMaxN = runtime.NumCPU() pollMaxN = runtime.NumCPU()
if pollMaxN > 8 { if pollMaxN > 8 {
......
...@@ -17,19 +17,32 @@ import ( ...@@ -17,19 +17,32 @@ import (
var initErr error var initErr error
// CancelIo Windows API cancels all outstanding IO for a particular
// socket on current thread. To overcome that limitation, we run
// special goroutine, locked to OS single thread, that both starts
// and cancels IO. It means, there are 2 unavoidable thread switches
// for every IO.
// Some newer versions of Windows has new CancelIoEx API, that does
// not have that limitation and can be used from any thread. This
// package uses CancelIoEx API, if present, otherwise it fallback
// to CancelIo.
var canCancelIO bool // determines if CancelIoEx API is present
func init() { func init() {
var d syscall.WSAData var d syscall.WSAData
e := syscall.WSAStartup(uint32(0x202), &d) e := syscall.WSAStartup(uint32(0x202), &d)
if e != nil { if e != nil {
initErr = os.NewSyscallError("WSAStartup", e) initErr = os.NewSyscallError("WSAStartup", e)
} }
canCancelIO = syscall.LoadCancelIoEx() == nil
} }
func closesocket(s syscall.Handle) error { func closesocket(s syscall.Handle) error {
return syscall.Closesocket(s) return syscall.Closesocket(s)
} }
// Interface for all io operations. // Interface for all IO operations.
type anOpIface interface { type anOpIface interface {
Op() *anOp Op() *anOp
Name() string Name() string
...@@ -42,7 +55,7 @@ type ioResult struct { ...@@ -42,7 +55,7 @@ type ioResult struct {
err error err error
} }
// anOp implements functionality common to all io operations. // anOp implements functionality common to all IO operations.
type anOp struct { type anOp struct {
// Used by IOCP interface, it must be first field // Used by IOCP interface, it must be first field
// of the struct, as our code rely on it. // of the struct, as our code rely on it.
...@@ -75,7 +88,7 @@ func (o *anOp) Op() *anOp { ...@@ -75,7 +88,7 @@ func (o *anOp) Op() *anOp {
return o return o
} }
// bufOp is used by io operations that read / write // bufOp is used by IO operations that read / write
// data from / to client buffer. // data from / to client buffer.
type bufOp struct { type bufOp struct {
anOp anOp
...@@ -92,7 +105,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte, mode int) { ...@@ -92,7 +105,7 @@ func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {
} }
} }
// resultSrv will retrieve all io completion results from // resultSrv will retrieve all IO completion results from
// iocp and send them to the correspondent waiting client // iocp and send them to the correspondent waiting client
// goroutine via channel supplied in the request. // goroutine via channel supplied in the request.
type resultSrv struct { type resultSrv struct {
...@@ -107,7 +120,7 @@ func (s *resultSrv) Run() { ...@@ -107,7 +120,7 @@ func (s *resultSrv) Run() {
r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)
switch { switch {
case r.err == nil: case r.err == nil:
// Dequeued successfully completed io packet. // Dequeued successfully completed IO packet.
case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:
// Wait has timed out (should not happen now, but might be used in the future). // Wait has timed out (should not happen now, but might be used in the future).
panic("GetQueuedCompletionStatus timed out") panic("GetQueuedCompletionStatus timed out")
...@@ -115,22 +128,23 @@ func (s *resultSrv) Run() { ...@@ -115,22 +128,23 @@ func (s *resultSrv) Run() {
// Failed to dequeue anything -> report the error. // Failed to dequeue anything -> report the error.
panic("GetQueuedCompletionStatus failed " + r.err.Error()) panic("GetQueuedCompletionStatus failed " + r.err.Error())
default: default:
// Dequeued failed io packet. // Dequeued failed IO packet.
} }
(*anOp)(unsafe.Pointer(o)).resultc <- r (*anOp)(unsafe.Pointer(o)).resultc <- r
} }
} }
// ioSrv executes net io requests. // ioSrv executes net IO requests.
type ioSrv struct { type ioSrv struct {
submchan chan anOpIface // submit io requests submchan chan anOpIface // submit IO requests
canchan chan anOpIface // cancel io requests canchan chan anOpIface // cancel IO requests
} }
// ProcessRemoteIO will execute submit io requests on behalf // ProcessRemoteIO will execute submit IO requests on behalf
// of other goroutines, all on a single os thread, so it can // of other goroutines, all on a single os thread, so it can
// cancel them later. Results of all operations will be sent // cancel them later. Results of all operations will be sent
// back to their requesters via channel supplied in request. // back to their requesters via channel supplied in request.
// It is used only when the CancelIoEx API is unavailable.
func (s *ioSrv) ProcessRemoteIO() { func (s *ioSrv) ProcessRemoteIO() {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()
...@@ -144,20 +158,21 @@ func (s *ioSrv) ProcessRemoteIO() { ...@@ -144,20 +158,21 @@ func (s *ioSrv) ProcessRemoteIO() {
} }
} }
// ExecIO executes a single io operation. It either executes it // ExecIO executes a single IO operation oi. It submits and cancels
// inline, or, if a deadline is employed, passes the request onto // IO in the current thread for systems where Windows CancelIoEx API
// is available. Alternatively, it passes the request onto
// a special goroutine and waits for completion or cancels request. // a special goroutine and waits for completion or cancels request.
// deadline is unix nanos. // deadline is unix nanos.
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
var err error var err error
o := oi.Op() o := oi.Op()
if deadline != 0 { if canCancelIO {
err = oi.Submit()
} else {
// Send request to a special dedicated thread, // Send request to a special dedicated thread,
// so it can stop the io with CancelIO later. // so it can stop the IO with CancelIO later.
s.submchan <- oi s.submchan <- oi
err = <-o.errnoc err = <-o.errnoc
} else {
err = oi.Submit()
} }
switch err { switch err {
case nil: case nil:
...@@ -168,27 +183,45 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { ...@@ -168,27 +183,45 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
default: default:
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err} return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
} }
// Wait for our request to complete. // Setup timer, if deadline is given.
var r ioResult var timer <-chan time.Time
if deadline != 0 { if deadline != 0 {
dt := deadline - time.Now().UnixNano() dt := deadline - time.Now().UnixNano()
if dt < 1 { if dt < 1 {
dt = 1 dt = 1
} }
timer := time.NewTimer(time.Duration(dt) * time.Nanosecond) t := time.NewTimer(time.Duration(dt) * time.Nanosecond)
defer timer.Stop() defer t.Stop()
select { timer = t.C
case r = <-o.resultc: }
case <-timer.C: // Wait for our request to complete.
var r ioResult
var cancelled bool
select {
case r = <-o.resultc:
case <-timer:
cancelled = true
case <-o.fd.closec:
cancelled = true
}
if cancelled {
// Cancel it.
if canCancelIO {
err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o)
// Assuming ERROR_NOT_FOUND is returned, if IO is completed.
if err != nil && err != syscall.ERROR_NOT_FOUND {
// TODO(brainman): maybe do something else, but panic.
panic(err)
}
} else {
s.canchan <- oi s.canchan <- oi
<-o.errnoc <-o.errnoc
r = <-o.resultc
if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
r.err = syscall.EWOULDBLOCK
}
} }
} else { // Wait for IO to be canceled or complete successfully.
r = <-o.resultc r = <-o.resultc
if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
r.err = syscall.EWOULDBLOCK
}
} }
if r.err != nil { if r.err != nil {
err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}
...@@ -211,9 +244,13 @@ func startServer() { ...@@ -211,9 +244,13 @@ func startServer() {
go resultsrv.Run() go resultsrv.Run()
iosrv = new(ioSrv) iosrv = new(ioSrv)
iosrv.submchan = make(chan anOpIface) if !canCancelIO {
iosrv.canchan = make(chan anOpIface) // Only CancelIo API is available. Lets start special goroutine
go iosrv.ProcessRemoteIO() // locked to an OS thread, that both starts and cancels IO.
iosrv.submchan = make(chan anOpIface)
iosrv.canchan = make(chan anOpIface)
go iosrv.ProcessRemoteIO()
}
} }
// Network file descriptor. // Network file descriptor.
...@@ -233,6 +270,7 @@ type netFD struct { ...@@ -233,6 +270,7 @@ type netFD struct {
raddr Addr raddr Addr
resultc [2]chan ioResult // read/write completion results resultc [2]chan ioResult // read/write completion results
errnoc [2]chan error // read/write submit or cancel operation errors errnoc [2]chan error // read/write submit or cancel operation errors
closec chan bool // used by Close to cancel pending IO
// owned by client // owned by client
rdeadline int64 rdeadline int64
...@@ -247,6 +285,7 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { ...@@ -247,6 +285,7 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
family: family, family: family,
sotype: sotype, sotype: sotype,
net: net, net: net,
closec: make(chan bool),
} }
runtime.SetFinalizer(netfd, (*netFD).Close) runtime.SetFinalizer(netfd, (*netFD).Close)
return netfd return netfd
...@@ -299,24 +338,12 @@ func (fd *netFD) incref(closing bool) error { ...@@ -299,24 +338,12 @@ func (fd *netFD) incref(closing bool) error {
// Remove a reference to this FD and close if we've been asked to do so (and // Remove a reference to this FD and close if we've been asked to do so (and
// there are no references left. // there are no references left.
func (fd *netFD) decref() { func (fd *netFD) decref() {
if fd == nil {
return
}
fd.sysmu.Lock() fd.sysmu.Lock()
fd.sysref-- fd.sysref--
// NOTE(rsc): On Unix we check fd.sysref == 0 here before closing, if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle {
// but on Windows we have no way to wake up the blocked I/O other
// than closing the socket (or calling Shutdown, which breaks other
// programs that might have a reference to the socket). So there is
// a small race here that we might close fd.sysfd and then some other
// goroutine might start a read of fd.sysfd (having read it before we
// write InvalidHandle to it), which might refer to some other file
// if the specific handle value gets reused. I think handle values on
// Windows are not reused as aggressively as file descriptors on Unix,
// so this might be tolerable.
if fd.closing && fd.sysfd != syscall.InvalidHandle {
// 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 resultsrv for Close too. Sigh.
syscall.SetNonblock(fd.sysfd, false)
closesocket(fd.sysfd) closesocket(fd.sysfd)
fd.sysfd = syscall.InvalidHandle fd.sysfd = syscall.InvalidHandle
// no need for a finalizer anymore // no need for a finalizer anymore
...@@ -329,7 +356,14 @@ func (fd *netFD) Close() error { ...@@ -329,7 +356,14 @@ func (fd *netFD) Close() error {
if err := fd.incref(true); err != nil { if err := fd.incref(true); err != nil {
return err return err
} }
fd.decref() defer fd.decref()
// unblock pending reader and writer
close(fd.closec)
// wait for both reader and writer to exit
fd.rio.Lock()
defer fd.rio.Unlock()
fd.wio.Lock()
defer fd.wio.Unlock()
return nil return nil
} }
...@@ -368,18 +402,12 @@ func (o *readOp) Name() string { ...@@ -368,18 +402,12 @@ func (o *readOp) Name() string {
} }
func (fd *netFD) Read(buf []byte) (int, error) { func (fd *netFD) Read(buf []byte) (int, error) {
if fd == nil {
return 0, syscall.EINVAL
}
fd.rio.Lock()
defer fd.rio.Unlock()
if err := fd.incref(false); err != nil { if err := fd.incref(false); err != nil {
return 0, err return 0, err
} }
defer fd.decref() defer fd.decref()
if fd.sysfd == syscall.InvalidHandle { fd.rio.Lock()
return 0, syscall.EINVAL defer fd.rio.Unlock()
}
var o readOp var o readOp
o.Init(fd, buf, 'r') o.Init(fd, buf, 'r')
n, err := iosrv.ExecIO(&o, fd.rdeadline) n, err := iosrv.ExecIO(&o, fd.rdeadline)
...@@ -407,18 +435,15 @@ func (o *readFromOp) Name() string { ...@@ -407,18 +435,15 @@ func (o *readFromOp) Name() string {
} }
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
if fd == nil {
return 0, nil, syscall.EINVAL
}
if len(buf) == 0 { if len(buf) == 0 {
return 0, nil, nil return 0, nil, nil
} }
fd.rio.Lock()
defer fd.rio.Unlock()
if err := fd.incref(false); err != nil { if err := fd.incref(false); err != nil {
return 0, nil, err return 0, nil, err
} }
defer fd.decref() defer fd.decref()
fd.rio.Lock()
defer fd.rio.Unlock()
var o readFromOp var o readFromOp
o.Init(fd, buf, 'r') o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa)) o.rsan = int32(unsafe.Sizeof(o.rsa))
...@@ -446,15 +471,12 @@ func (o *writeOp) Name() string { ...@@ -446,15 +471,12 @@ func (o *writeOp) Name() string {
} }
func (fd *netFD) Write(buf []byte) (int, error) { func (fd *netFD) Write(buf []byte) (int, error) {
if fd == nil {
return 0, syscall.EINVAL
}
fd.wio.Lock()
defer fd.wio.Unlock()
if err := fd.incref(false); err != nil { if err := fd.incref(false); err != nil {
return 0, err return 0, err
} }
defer fd.decref() defer fd.decref()
fd.wio.Lock()
defer fd.wio.Unlock()
var o writeOp var o writeOp
o.Init(fd, buf, 'w') o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline) return iosrv.ExecIO(&o, fd.wdeadline)
...@@ -477,21 +499,15 @@ func (o *writeToOp) Name() string { ...@@ -477,21 +499,15 @@ func (o *writeToOp) Name() string {
} }
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
if fd == nil {
return 0, syscall.EINVAL
}
if len(buf) == 0 { if len(buf) == 0 {
return 0, nil return 0, nil
} }
fd.wio.Lock()
defer fd.wio.Unlock()
if err := fd.incref(false); err != nil { if err := fd.incref(false); err != nil {
return 0, err return 0, err
} }
defer fd.decref() defer fd.decref()
if fd.sysfd == syscall.InvalidHandle { fd.wio.Lock()
return 0, syscall.EINVAL defer fd.wio.Unlock()
}
var o writeToOp var o writeToOp
o.Init(fd, buf, 'w') o.Init(fd, buf, 'w')
o.sa = sa o.sa = sa
......
...@@ -174,3 +174,42 @@ func TestUDPListenClose(t *testing.T) { ...@@ -174,3 +174,42 @@ func TestUDPListenClose(t *testing.T) {
t.Fatal("timeout waiting for UDP close") t.Fatal("timeout waiting for UDP close")
} }
} }
func TestTCPClose(t *testing.T) {
l, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
read := func(r io.Reader) error {
var m [1]byte
_, err := r.Read(m[:])
return err
}
go func() {
c, err := Dial("tcp", l.Addr().String())
if err != nil {
t.Fatal(err)
}
go read(c)
time.Sleep(10 * time.Millisecond)
c.Close()
}()
c, err := l.Accept()
if err != nil {
t.Fatal(err)
}
defer c.Close()
for err == nil {
err = read(c)
}
if err != nil && err != io.EOF {
t.Fatal(err)
}
}
...@@ -499,6 +499,27 @@ func TestClientWriteError(t *testing.T) { ...@@ -499,6 +499,27 @@ func TestClientWriteError(t *testing.T) {
w.done <- true w.done <- true
} }
func TestTCPClose(t *testing.T) {
once.Do(startServer)
client, err := dialHTTP()
if err != nil {
t.Fatalf("dialing: %v", err)
}
defer client.Close()
args := Args{17, 8}
var reply Reply
err = client.Call("Arith.Mul", args, &reply)
if err != nil {
t.Fatal("arith error:", err)
}
t.Logf("Arith: %d*%d=%d\n", args.A, args.B, reply)
if reply.C != args.A*args.B {
t.Errorf("Add: expected %d got %d", reply.C, args.A*args.B)
}
}
func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) { func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
b.StopTimer() b.StopTimer()
once.Do(startServer) once.Do(startServer)
......
...@@ -48,12 +48,12 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { ...@@ -48,12 +48,12 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
return 0, nil, false return 0, nil, false
} }
c.wio.Lock()
defer c.wio.Unlock()
if err := c.incref(false); err != nil { if err := c.incref(false); err != nil {
return 0, err, true return 0, err, true
} }
defer c.decref() defer c.decref()
c.wio.Lock()
defer c.wio.Unlock()
var o sendfileOp var o sendfileOp
o.Init(c, 'w') o.Init(c, 'w')
......
...@@ -146,3 +146,82 @@ func TestTimeoutAccept(t *testing.T) { ...@@ -146,3 +146,82 @@ func TestTimeoutAccept(t *testing.T) {
// Pass. // Pass.
} }
} }
func TestReadWriteDeadline(t *testing.T) {
if !canCancelIO {
t.Logf("skipping test on this system")
return
}
const (
readTimeout = 100 * time.Millisecond
writeTimeout = 200 * time.Millisecond
delta = 40 * time.Millisecond
)
checkTimeout := func(command string, start time.Time, should time.Duration) {
is := time.Now().Sub(start)
d := should - is
if d < -delta || delta < d {
t.Errorf("%s timeout test failed: is=%v should=%v\n", command, is, should)
}
}
ln, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("ListenTCP on :0: %v", err)
}
lnquit := make(chan bool)
go func() {
c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
}
defer c.Close()
lnquit <- true
}()
c, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer c.Close()
start := time.Now()
err = c.SetReadDeadline(start.Add(readTimeout))
if err != nil {
t.Fatalf("SetReadDeadline: %v", err)
}
err = c.SetWriteDeadline(start.Add(writeTimeout))
if err != nil {
t.Fatalf("SetWriteDeadline: %v", err)
}
quit := make(chan bool)
go func() {
var buf [10]byte
_, err = c.Read(buf[:])
if err == nil {
t.Errorf("Read should not succeed")
}
checkTimeout("Read", start, readTimeout)
quit <- true
}()
go func() {
var buf [10000]byte
for {
_, err = c.Write(buf[:])
if err != nil {
break
}
}
checkTimeout("Write", start, writeTimeout)
quit <- true
}()
<-quit
<-quit
<-lnquit
}
...@@ -142,6 +142,7 @@ func NewCallback(fn interface{}) uintptr ...@@ -142,6 +142,7 @@ func NewCallback(fn interface{}) uintptr
//sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error) //sys GetQueuedCompletionStatus(cphandle Handle, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (err error)
//sys PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error) //sys PostQueuedCompletionStatus(cphandle Handle, qty uint32, key uint32, overlapped *Overlapped) (err error)
//sys CancelIo(s Handle) (err error) //sys CancelIo(s Handle) (err error)
//sys CancelIoEx(s Handle, o *Overlapped) (err error)
//sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW
//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error) //sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error)
//sys TerminateProcess(handle Handle, exitcode uint32) (err error) //sys TerminateProcess(handle Handle, exitcode uint32) (err error)
...@@ -474,6 +475,10 @@ func Chmod(path string, mode uint32) (err error) { ...@@ -474,6 +475,10 @@ func Chmod(path string, mode uint32) (err error) {
return SetFileAttributes(p, attrs) return SetFileAttributes(p, attrs)
} }
func LoadCancelIoEx() error {
return procCancelIoEx.Find()
}
// net api calls // net api calls
const socket_error = uintptr(^uint32(0)) const socket_error = uintptr(^uint32(0))
......
...@@ -49,6 +49,7 @@ var ( ...@@ -49,6 +49,7 @@ var (
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo") procCancelIo = modkernel32.NewProc("CancelIo")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateProcessW = modkernel32.NewProc("CreateProcessW") procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess") procOpenProcess = modkernel32.NewProc("OpenProcess")
procTerminateProcess = modkernel32.NewProc("TerminateProcess") procTerminateProcess = modkernel32.NewProc("TerminateProcess")
...@@ -535,6 +536,18 @@ func CancelIo(s Handle) (err error) { ...@@ -535,6 +536,18 @@ func CancelIo(s Handle) (err error) {
return return
} }
func CancelIoEx(s Handle, o *Overlapped) (err error) {
r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = EINVAL
}
}
return
}
func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) { func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) {
var _p0 uint32 var _p0 uint32
if inheritHandles { if inheritHandles {
......
...@@ -49,6 +49,7 @@ var ( ...@@ -49,6 +49,7 @@ var (
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus")
procCancelIo = modkernel32.NewProc("CancelIo") procCancelIo = modkernel32.NewProc("CancelIo")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateProcessW = modkernel32.NewProc("CreateProcessW") procCreateProcessW = modkernel32.NewProc("CreateProcessW")
procOpenProcess = modkernel32.NewProc("OpenProcess") procOpenProcess = modkernel32.NewProc("OpenProcess")
procTerminateProcess = modkernel32.NewProc("TerminateProcess") procTerminateProcess = modkernel32.NewProc("TerminateProcess")
...@@ -535,6 +536,18 @@ func CancelIo(s Handle) (err error) { ...@@ -535,6 +536,18 @@ func CancelIo(s Handle) (err error) {
return return
} }
func CancelIoEx(s Handle, o *Overlapped) (err error) {
r1, _, e1 := Syscall(procCancelIoEx.Addr(), 2, uintptr(s), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = EINVAL
}
}
return
}
func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) { func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) {
var _p0 uint32 var _p0 uint32
if inheritHandles { if inheritHandles {
......
...@@ -20,6 +20,7 @@ const ( ...@@ -20,6 +20,7 @@ const (
ERROR_ENVVAR_NOT_FOUND Errno = 203 ERROR_ENVVAR_NOT_FOUND Errno = 203
ERROR_OPERATION_ABORTED Errno = 995 ERROR_OPERATION_ABORTED Errno = 995
ERROR_IO_PENDING Errno = 997 ERROR_IO_PENDING Errno = 997
ERROR_NOT_FOUND Errno = 1168
) )
const ( const (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment