summaryrefslogtreecommitdiff
path: root/src/runtime/netpoll_windows.c
blob: 64da41ad9049af21acdcdf194d0a9aabb0f789f7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
// 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"
#pragma dynimport runtime·WSAGetOverlappedResult WSAGetOverlappedResult "ws2_32.dll"

extern void *runtime·CreateIoCompletionPort;
extern void *runtime·GetQueuedCompletionStatus;
extern void *runtime·WSAGetOverlappedResult;

#define INVALID_HANDLE_VALUE ((uintptr)-1)

// net_op must be the same as beginning of net.operation. Keep these in sync.
typedef struct net_op net_op;
struct net_op
{
	// used by windows
	Overlapped	o;
	// used by netpoll
	PollDesc*	pd;
	int32	mode;
	int32	errno;
	uint32	qty;
};

typedef struct OverlappedEntry OverlappedEntry;
struct OverlappedEntry
{
	uintptr	key;
	net_op*	op;  // In reality it's Overlapped*, but we cast it to net_op* anyway.
	uintptr	internal;
	uint32	qty;
};

static void handlecompletion(G **gpp, net_op *o, int32 errno, uint32 qty);

static uintptr iocphandle = INVALID_HANDLE_VALUE;  // completion port io handle

void
runtime·netpollinit(void)
{
	iocphandle = (uintptr)runtime·stdcall4(runtime·CreateIoCompletionPort, INVALID_HANDLE_VALUE, 0, 0, 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·stdcall4(runtime·CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0)
		return -runtime·getlasterror();
	return 0;
}

int32
runtime·netpollclose(uintptr fd)
{
	// nothing to do
	USED(fd);
	return 0;
}

void
runtime·netpollarm(PollDesc* pd, int32 mode)
{
	USED(pd, mode);
	runtime·throw("unused");
}

// Polls for completed network IO.
// Returns list of goroutines that become runnable.
G*
runtime·netpoll(bool block)
{
	OverlappedEntry entries[64];
	uint32 wait, qty, key, flags, n, i;
	int32 errno;
	net_op *op;
	G *gp;

	if(iocphandle == INVALID_HANDLE_VALUE)
		return nil;
	gp = nil;
	wait = 0;
	if(block)
		wait = INFINITE;
retry:
	if(runtime·GetQueuedCompletionStatusEx != nil) {
		n = nelem(entries) / runtime·gomaxprocs;
		if(n < 8)
			n = 8;
		if(block)
			g->m->blocked = true;
		if(runtime·stdcall6(runtime·GetQueuedCompletionStatusEx, iocphandle, (uintptr)entries, n, (uintptr)&n, wait, 0) == 0) {
			g->m->blocked = false;
			errno = runtime·getlasterror();
			if(!block && errno == WAIT_TIMEOUT)
				return nil;
			runtime·printf("netpoll: GetQueuedCompletionStatusEx failed (errno=%d)\n", errno);
			runtime·throw("netpoll: GetQueuedCompletionStatusEx failed");
		}
		g->m->blocked = false;
		for(i = 0; i < n; i++) {
			op = entries[i].op;
			errno = 0;
			qty = 0;
			if(runtime·stdcall5(runtime·WSAGetOverlappedResult, runtime·netpollfd(op->pd), (uintptr)op, (uintptr)&qty, 0, (uintptr)&flags) == 0)
				errno = runtime·getlasterror();
			handlecompletion(&gp, op, errno, qty);
		}
	} else {
		op = nil;
		errno = 0;
		qty = 0;
		if(block)
			g->m->blocked = true;
		if(runtime·stdcall5(runtime·GetQueuedCompletionStatus, iocphandle, (uintptr)&qty, (uintptr)&key, (uintptr)&op, wait) == 0) {
			g->m->blocked = false;
			errno = runtime·getlasterror();
			if(!block && errno == WAIT_TIMEOUT)
				return nil;
			if(op == nil) {
				runtime·printf("netpoll: GetQueuedCompletionStatus failed (errno=%d)\n", errno);
				runtime·throw("netpoll: GetQueuedCompletionStatus failed");
			}
			// dequeued failed IO packet, so report that
		}
		g->m->blocked = false;
		handlecompletion(&gp, op, errno, qty);
	}
	if(block && gp == nil)
		goto retry;
	return gp;
}

static void
handlecompletion(G **gpp, net_op *op, int32 errno, uint32 qty)
{
	int32 mode;

	if(op == nil)
		runtime·throw("netpoll: GetQueuedCompletionStatus returned op == nil");
	mode = op->mode;
	if(mode != 'r' && mode != 'w') {
		runtime·printf("netpoll: GetQueuedCompletionStatus returned invalid mode=%d\n", mode);
		runtime·throw("netpoll: GetQueuedCompletionStatus returned invalid mode");
	}
	op->errno = errno;
	op->qty = qty;
	runtime·netpollready(gpp, op->pd, mode);
}