net: implement netpoll for windows
Moves the network poller from net package into runtime.
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 316386 287061 -9.27%
BenchmarkTCP4OneShot-2 339822 313424 -7.77%
BenchmarkTCP4OneShot-3 330057 306589 -7.11%
BenchmarkTCP4OneShotTimeout 341775 287061 -16.01%
BenchmarkTCP4OneShotTimeout-2 380835 295849 -22.32%
BenchmarkTCP4OneShotTimeout-3 398412 328070 -17.66%
BenchmarkTCP4Persistent 40622 33392 -17.80%
BenchmarkTCP4Persistent-2 44528 35736 -19.74%
BenchmarkTCP4Persistent-3 44919 36907 -17.84%
BenchmarkTCP4PersistentTimeout 45309 33588 -25.87%
BenchmarkTCP4PersistentTimeout-2 50289 38079 -24.28%
BenchmarkTCP4PersistentTimeout-3 51559 37103 -28.04%
BenchmarkTCP6OneShot 361305 345645 -4.33%
BenchmarkTCP6OneShot-2 361305 331976 -8.12%
BenchmarkTCP6OneShot-3 376929 347598 -7.78%
BenchmarkTCP6OneShotTimeout 361305 322212 -10.82%
BenchmarkTCP6OneShotTimeout-2 378882 333928 -11.86%
BenchmarkTCP6OneShotTimeout-3 388647 335881 -13.58%
BenchmarkTCP6Persistent 47653 35345 -25.83%
BenchmarkTCP6Persistent-2 49215 35736 -27.39%
BenchmarkTCP6Persistent-3 38474 37493 -2.55%
BenchmarkTCP6PersistentTimeout 56637 34369 -39.32%
BenchmarkTCP6PersistentTimeout-2 42575 38079 -10.56%
BenchmarkTCP6PersistentTimeout-3 44137 37689 -14.61%
R=dvyukov
CC=golang-dev
https://golang.org/cl/8670044
diff --git a/src/pkg/runtime/defs_windows.go b/src/pkg/runtime/defs_windows.go
index 0d525b9..01aea92 100644
--- a/src/pkg/runtime/defs_windows.go
+++ b/src/pkg/runtime/defs_windows.go
@@ -7,8 +7,8 @@
/*
Input to cgo.
-GOARCH=amd64 cgo -cdefs defs.go >amd64/defs.h
-GOARCH=386 cgo -cdefs defs.go >386/defs.h
+GOARCH=amd64 go tool cgo -cdefs defs_windows.go > defs_windows_amd64.h
+GOARCH=386 go tool cgo -cdefs defs_windows.go > defs_windows_386.h
*/
package runtime
@@ -57,6 +57,9 @@
EXCEPTION_FLT_UNDERFLOW = C.STATUS_FLOAT_UNDERFLOW
EXCEPTION_INT_DIVIDE_BY_ZERO = C.STATUS_INTEGER_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW = C.STATUS_INTEGER_OVERFLOW
+
+ INFINITE = C.INFINITE
+ WAIT_TIMEOUT = C.WAIT_TIMEOUT
)
type SystemInfo C.SYSTEM_INFO
@@ -64,3 +67,4 @@
type FloatingSaveArea C.FLOATING_SAVE_AREA
type M128a C.M128A
type Context C.CONTEXT
+type Overlapped C.OVERLAPPED
diff --git a/src/pkg/runtime/defs_windows_386.h b/src/pkg/runtime/defs_windows_386.h
index 3377db9..db3629a 100644
--- a/src/pkg/runtime/defs_windows_386.h
+++ b/src/pkg/runtime/defs_windows_386.h
@@ -30,6 +30,9 @@
EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
EXCEPTION_INT_OVERFLOW = 0xc0000095,
+
+ INFINITE = 0xffffffff,
+ WAIT_TIMEOUT = 0x102,
};
typedef struct SystemInfo SystemInfo;
@@ -37,6 +40,7 @@
typedef struct FloatingSaveArea FloatingSaveArea;
typedef struct M128a M128a;
typedef struct Context Context;
+typedef struct Overlapped Overlapped;
#pragma pack on
@@ -98,6 +102,12 @@
uint32 SegSs;
uint8 ExtendedRegisters[512];
};
+struct Overlapped {
+ uint32 Internal;
+ uint32 InternalHigh;
+ byte anon0[8];
+ byte *hEvent;
+};
#pragma pack off
diff --git a/src/pkg/runtime/defs_windows_amd64.h b/src/pkg/runtime/defs_windows_amd64.h
index c0a99ea..fe26f5a 100644
--- a/src/pkg/runtime/defs_windows_amd64.h
+++ b/src/pkg/runtime/defs_windows_amd64.h
@@ -30,6 +30,9 @@
EXCEPTION_FLT_UNDERFLOW = 0xc0000093,
EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094,
EXCEPTION_INT_OVERFLOW = 0xc0000095,
+
+ INFINITE = 0xffffffff,
+ WAIT_TIMEOUT = 0x102,
};
typedef struct SystemInfo SystemInfo;
@@ -37,6 +40,7 @@
typedef struct FloatingSaveArea FloatingSaveArea;
typedef struct M128a M128a;
typedef struct Context Context;
+typedef struct Overlapped Overlapped;
#pragma pack on
@@ -113,6 +117,12 @@
uint64 LastExceptionToRip;
uint64 LastExceptionFromRip;
};
+struct Overlapped {
+ uint64 Internal;
+ uint64 InternalHigh;
+ byte anon0[8];
+ byte *hEvent;
+};
#pragma pack off
diff --git a/src/pkg/runtime/netpoll.goc b/src/pkg/runtime/netpoll.goc
index e9c0218..66557cc 100644
--- a/src/pkg/runtime/netpoll.goc
+++ b/src/pkg/runtime/netpoll.goc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build darwin linux
+// +build darwin linux windows
package net
diff --git a/src/pkg/runtime/netpoll_stub.c b/src/pkg/runtime/netpoll_stub.c
index 39d19a4c..c6ecf67 100644
--- a/src/pkg/runtime/netpoll_stub.c
+++ b/src/pkg/runtime/netpoll_stub.c
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build freebsd netbsd openbsd plan9 windows
+// +build freebsd netbsd openbsd plan9
#include "runtime.h"
diff --git a/src/pkg/runtime/netpoll_windows.c b/src/pkg/runtime/netpoll_windows.c
new file mode 100644
index 0000000..52ba7e4
--- /dev/null
+++ b/src/pkg/runtime/netpoll_windows.c
@@ -0,0 +1,110 @@
+// Copyright 2013 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.
+
+#include "runtime.h"
+#include "defs_GOOS_GOARCH.h"
+#include "os_GOOS.h"
+
+#define DWORD_MAX 0xffffffff
+
+#pragma dynimport runtime·CreateIoCompletionPort CreateIoCompletionPort "kernel32.dll"
+#pragma dynimport runtime·GetQueuedCompletionStatus GetQueuedCompletionStatus "kernel32.dll"
+
+extern void *runtime·CreateIoCompletionPort;
+extern void *runtime·GetQueuedCompletionStatus;
+
+#define INVALID_HANDLE_VALUE ((uintptr)-1)
+
+// net_anOp must be the same as beginning of net.anOp. Keep these in sync.
+typedef struct net_anOp net_anOp;
+struct net_anOp
+{
+ // used by windows
+ Overlapped o;
+ // used by netpoll
+ uintptr runtimeCtx;
+ int32 mode;
+ int32 errno;
+ uint32 qty;
+};
+
+static uintptr iocphandle = INVALID_HANDLE_VALUE; // completion port io handle
+
+void
+runtime·netpollinit(void)
+{
+ iocphandle = (uintptr)runtime·stdcall(runtime·CreateIoCompletionPort, 4, INVALID_HANDLE_VALUE, (uintptr)0, (uintptr)0, (uintptr)DWORD_MAX);
+ if(iocphandle == 0) {
+ runtime·printf("netpoll: failed to create iocp handle (errno=%d)\n", runtime·getlasterror());
+ runtime·throw("netpoll: failed to create iocp handle");
+ }
+ return;
+}
+
+int32
+runtime·netpollopen(uintptr fd, PollDesc *pd)
+{
+ USED(pd);
+ if(runtime·stdcall(runtime·CreateIoCompletionPort, 4, fd, iocphandle, (uintptr)0, (uintptr)0) == 0)
+ return -runtime·getlasterror();
+ return 0;
+}
+
+int32
+runtime·netpollclose(uintptr fd)
+{
+ // nothing to do
+ USED(fd);
+ return 0;
+}
+
+// Polls for completed network IO.
+// Returns list of goroutines that become runnable.
+G*
+runtime·netpoll(bool block)
+{
+ uint32 wait, qty, key;
+ int32 mode, errno;
+ net_anOp *o;
+ G *gp;
+
+ if(iocphandle == INVALID_HANDLE_VALUE)
+ return nil;
+ o = nil;
+ errno = 0;
+ qty = 0;
+ wait = INFINITE;
+ if(!block)
+ // TODO(brainman): should use 0 here instead, but scheduler hogs CPU
+ wait = 1;
+ // TODO(brainman): Need a loop here to fetch all pending notifications
+ // (or at least a batch). Scheduler will behave better if is given
+ // a batch of newly runnable goroutines.
+ // TODO(brainman): Call GetQueuedCompletionStatusEx() here when possible.
+ if(runtime·stdcall(runtime·GetQueuedCompletionStatus, 5, iocphandle, &qty, &key, &o, (uintptr)wait) == 0) {
+ errno = runtime·getlasterror();
+ if(o == nil && errno == WAIT_TIMEOUT) {
+ if(!block)
+ return nil;
+ runtime·throw("netpoll: GetQueuedCompletionStatus timed out");
+ }
+ if(o == nil) {
+ runtime·printf("netpoll: GetQueuedCompletionStatus failed (errno=%d)\n", errno);
+ runtime·throw("netpoll: GetQueuedCompletionStatus failed");
+ }
+ // dequeued failed IO packet, so report that
+ }
+ if(o == nil)
+ runtime·throw("netpoll: GetQueuedCompletionStatus returned o == nil");
+ mode = o->mode;
+ if(mode != 'r' && mode != 'w') {
+ runtime·printf("netpoll: GetQueuedCompletionStatus returned invalid mode=%d\n", mode);
+ runtime·throw("netpoll: GetQueuedCompletionStatus returned invalid mode");
+ }
+ o->errno = errno;
+ o->qty = qty;
+ gp = nil;
+ runtime·netpollready(&gp, (void*)o->runtimeCtx, mode);
+ return gp;
+}