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 = >fds;
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