1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 
5 // +build solaris
6 
7 #include "config.h"
8 
9 #include <errno.h>
10 #include <sys/times.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 
15 #ifdef HAVE_SYS_SELECT_H
16 #include <sys/select.h>
17 #endif
18 
19 #include "runtime.h"
20 #include "malloc.h"
21 
22 static Lock selectlock;
23 static int rdwake;
24 static int wrwake;
25 static fd_set fds;
26 static PollDesc **data;
27 static int allocated;
28 
29 void
runtime_netpollinit(void)30 runtime_netpollinit(void)
31 {
32 	int p[2];
33 	int fl;
34 
35 	FD_ZERO(&fds);
36 	allocated = 128;
37 	data = runtime_mallocgc(allocated * sizeof(PollDesc *), 0,
38 				FlagNoScan|FlagNoProfiling|FlagNoInvokeGC);
39 
40 	if(pipe(p) < 0)
41 		runtime_throw("netpollinit: failed to create pipe");
42 	rdwake = p[0];
43 	wrwake = p[1];
44 
45 	fl = fcntl(rdwake, F_GETFL);
46 	if(fl < 0)
47 		runtime_throw("netpollinit: fcntl failed");
48 	fl |= O_NONBLOCK;
49 	if(fcntl(rdwake, F_SETFL, fl))
50 		 runtime_throw("netpollinit: fcntl failed");
51 	fcntl(rdwake, F_SETFD, FD_CLOEXEC);
52 
53 	fl = fcntl(wrwake, F_GETFL);
54 	if(fl < 0)
55 		runtime_throw("netpollinit: fcntl failed");
56 	fl |= O_NONBLOCK;
57 	if(fcntl(wrwake, F_SETFL, fl))
58 		 runtime_throw("netpollinit: fcntl failed");
59 	fcntl(wrwake, F_SETFD, FD_CLOEXEC);
60 
61 	FD_SET(rdwake, &fds);
62 }
63 
64 int32
runtime_netpollopen(uintptr fd,PollDesc * pd)65 runtime_netpollopen(uintptr fd, PollDesc *pd)
66 {
67 	byte b;
68 
69 	runtime_lock(&selectlock);
70 
71 	if((int)fd >= allocated) {
72 		int c;
73 		PollDesc **n;
74 
75 		c = allocated;
76 
77 		runtime_unlock(&selectlock);
78 
79 		while((int)fd >= c)
80 			c *= 2;
81 		n = runtime_mallocgc(c * sizeof(PollDesc *), 0,
82 				     FlagNoScan|FlagNoProfiling|FlagNoInvokeGC);
83 
84 		runtime_lock(&selectlock);
85 
86 		if(c > allocated) {
87 			__builtin_memcpy(n, data, allocated * sizeof(PollDesc *));
88 			allocated = c;
89 			data = n;
90 		}
91 	}
92 	FD_SET(fd, &fds);
93 	data[fd] = pd;
94 
95 	runtime_unlock(&selectlock);
96 
97 	b = 0;
98 	write(wrwake, &b, sizeof b);
99 
100 	return 0;
101 }
102 
103 int32
runtime_netpollclose(uintptr fd)104 runtime_netpollclose(uintptr fd)
105 {
106 	byte b;
107 
108 	runtime_lock(&selectlock);
109 
110 	FD_CLR(fd, &fds);
111 	data[fd] = nil;
112 
113 	runtime_unlock(&selectlock);
114 
115 	b = 0;
116 	write(wrwake, &b, sizeof b);
117 
118 	return 0;
119 }
120 
121 /* Used to avoid using too much stack memory.  */
122 static bool inuse;
123 static fd_set grfds, gwfds, gefds, gtfds;
124 
125 G*
runtime_netpoll(bool block)126 runtime_netpoll(bool block)
127 {
128 	fd_set *prfds, *pwfds, *pefds, *ptfds;
129 	bool allocatedfds;
130 	struct timeval timeout;
131 	struct timeval *pt;
132 	int max, c, i;
133 	G *gp;
134 	int32 mode;
135 	byte b;
136 	struct stat st;
137 
138 	allocatedfds = false;
139 
140  retry:
141 	runtime_lock(&selectlock);
142 
143 	max = allocated;
144 
145 	if(max == 0) {
146 		runtime_unlock(&selectlock);
147 		return nil;
148 	}
149 
150 	if(inuse) {
151 		if(!allocatedfds) {
152 			prfds = runtime_SysAlloc(4 * sizeof fds, &mstats.other_sys);
153 			pwfds = prfds + 1;
154 			pefds = pwfds + 1;
155 			ptfds = pefds + 1;
156 			allocatedfds = true;
157 		}
158 	} else {
159 		prfds = &grfds;
160 		pwfds = &gwfds;
161 		pefds = &gefds;
162 		ptfds = &gtfds;
163 		inuse = true;
164 		allocatedfds = false;
165 	}
166 
167 	__builtin_memcpy(prfds, &fds, sizeof fds);
168 
169 	runtime_unlock(&selectlock);
170 
171 	__builtin_memcpy(pwfds, prfds, sizeof fds);
172 	FD_CLR(rdwake, pwfds);
173 	__builtin_memcpy(pefds, pwfds, sizeof fds);
174 
175 	__builtin_memcpy(ptfds, pwfds, sizeof fds);
176 
177 	__builtin_memset(&timeout, 0, sizeof timeout);
178 	pt = &timeout;
179 	if(block)
180 		pt = nil;
181 
182 	c = select(max, prfds, pwfds, pefds, pt);
183 	if(c < 0) {
184 		if(errno == EBADF) {
185 			// Some file descriptor has been closed.
186 			// Check each one, and treat each closed
187 			// descriptor as ready for read/write.
188 			c = 0;
189 			FD_ZERO(prfds);
190 			FD_ZERO(pwfds);
191 			FD_ZERO(pefds);
192 			for(i = 0; i < max; i++) {
193 				if(FD_ISSET(i, ptfds)
194 				   && fstat(i, &st) < 0
195 				   && errno == EBADF) {
196 					FD_SET(i, prfds);
197 					FD_SET(i, pwfds);
198 					c += 2;
199 				}
200 			}
201 		}
202 		else {
203 			if(errno != EINTR)
204 				runtime_printf("runtime: select failed with %d\n", errno);
205 			goto retry;
206 		}
207 	}
208 	gp = nil;
209 	for(i = 0; i < max && c > 0; i++) {
210 		mode = 0;
211 		if(FD_ISSET(i, prfds)) {
212 			mode += 'r';
213 			--c;
214 		}
215 		if(FD_ISSET(i, pwfds)) {
216 			mode += 'w';
217 			--c;
218 		}
219 		if(FD_ISSET(i, pefds)) {
220 			mode = 'r' + 'w';
221 			--c;
222 		}
223 		if(i == rdwake && mode != 0) {
224 			while(read(rdwake, &b, sizeof b) > 0)
225 				;
226 			continue;
227 		}
228 		if(mode) {
229 			PollDesc *pd;
230 
231 			runtime_lock(&selectlock);
232 			pd = data[i];
233 			runtime_unlock(&selectlock);
234 			if(pd != nil)
235 				runtime_netpollready(&gp, pd, mode);
236 		}
237 	}
238 	if(block && gp == nil)
239 		goto retry;
240 
241 	if(allocatedfds) {
242 		runtime_SysFree(prfds, 4 * sizeof fds, &mstats.other_sys);
243 	} else {
244 		runtime_lock(&selectlock);
245 		inuse = false;
246 		runtime_unlock(&selectlock);
247 	}
248 
249 	return gp;
250 }
251 
252 void
runtime_netpoll_scan(struct Workbuf ** wbufp,void (* enqueue1)(struct Workbuf **,Obj))253 runtime_netpoll_scan(struct Workbuf** wbufp, void (*enqueue1)(struct Workbuf**, Obj))
254 {
255 	enqueue1(wbufp, (Obj){(byte*)&data, sizeof data, 0});
256 }
257