1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 * This file implements _PR_MD_PR_POLL for Win32.
8 */
9
10 /* The default value of FD_SETSIZE is 64. */
11 #define FD_SETSIZE 1024
12
13 #include "primpl.h"
14
15 #if !defined(_PR_GLOBAL_THREADS_ONLY)
16
17 struct select_data_s {
18 PRInt32 status;
19 PRInt32 error;
20 fd_set *rd, *wt, *ex;
21 const struct timeval *tv;
22 };
23
24 static void
_PR_MD_select_thread(void * cdata)25 _PR_MD_select_thread(void *cdata)
26 {
27 struct select_data_s *cd = (struct select_data_s *)cdata;
28
29 cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
30
31 if (cd->status == SOCKET_ERROR) {
32 cd->error = WSAGetLastError();
33 }
34 }
35
_PR_NTFiberSafeSelect(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,const struct timeval * timeout)36 int _PR_NTFiberSafeSelect(
37 int nfds,
38 fd_set *readfds,
39 fd_set *writefds,
40 fd_set *exceptfds,
41 const struct timeval *timeout)
42 {
43 PRThread *me = _PR_MD_CURRENT_THREAD();
44 int ready;
45
46 if (_PR_IS_NATIVE_THREAD(me)) {
47 ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout);
48 }
49 else
50 {
51 /*
52 ** Creating a new thread on each call!!
53 ** I guess web server doesn't use non-block I/O.
54 */
55 PRThread *selectThread;
56 struct select_data_s data;
57 data.status = 0;
58 data.error = 0;
59 data.rd = readfds;
60 data.wt = writefds;
61 data.ex = exceptfds;
62 data.tv = timeout;
63
64 selectThread = PR_CreateThread(
65 PR_USER_THREAD, _PR_MD_select_thread, &data,
66 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
67 if (selectThread == NULL) {
68 return -1;
69 }
70
71 PR_JoinThread(selectThread);
72 ready = data.status;
73 if (ready == SOCKET_ERROR) {
74 WSASetLastError(data.error);
75 }
76 }
77 return ready;
78 }
79
80 #endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
81
_PR_MD_PR_POLL(PRPollDesc * pds,PRIntn npds,PRIntervalTime timeout)82 PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
83 {
84 int ready, err;
85 fd_set rd, wt, ex;
86 fd_set *rdp, *wtp, *exp;
87 int nrd, nwt, nex;
88 PRFileDesc *bottom;
89 PRPollDesc *pd, *epd;
90 PRThread *me = _PR_MD_CURRENT_THREAD();
91
92 struct timeval tv, *tvp = NULL;
93
94 if (_PR_PENDING_INTERRUPT(me))
95 {
96 me->flags &= ~_PR_INTERRUPT;
97 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
98 return -1;
99 }
100
101 /*
102 ** Is it an empty set? If so, just sleep for the timeout and return
103 */
104 if (0 == npds)
105 {
106 PR_Sleep(timeout);
107 return 0;
108 }
109
110 nrd = nwt = nex = 0;
111 FD_ZERO(&rd);
112 FD_ZERO(&wt);
113 FD_ZERO(&ex);
114
115 ready = 0;
116 for (pd = pds, epd = pd + npds; pd < epd; pd++)
117 {
118 SOCKET osfd;
119 PRInt16 in_flags_read = 0, in_flags_write = 0;
120 PRInt16 out_flags_read = 0, out_flags_write = 0;
121
122 if ((NULL != pd->fd) && (0 != pd->in_flags))
123 {
124 if (pd->in_flags & PR_POLL_READ)
125 {
126 in_flags_read = (pd->fd->methods->poll)(
127 pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
128 &out_flags_read);
129 }
130 if (pd->in_flags & PR_POLL_WRITE)
131 {
132 in_flags_write = (pd->fd->methods->poll)(
133 pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
134 &out_flags_write);
135 }
136 if ((0 != (in_flags_read & out_flags_read))
137 || (0 != (in_flags_write & out_flags_write)))
138 {
139 /* this one's ready right now (buffered input) */
140 if (0 == ready)
141 {
142 /*
143 * We will have to return without calling the
144 * system poll/select function. So zero the
145 * out_flags fields of all the poll descriptors
146 * before this one.
147 */
148 PRPollDesc *prev;
149 for (prev = pds; prev < pd; prev++)
150 {
151 prev->out_flags = 0;
152 }
153 }
154 ready += 1;
155 pd->out_flags = out_flags_read | out_flags_write;
156 }
157 else
158 {
159 pd->out_flags = 0; /* pre-condition */
160 /* make sure this is an NSPR supported stack */
161 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
162 /* ignore a socket without PR_NSPR_IO_LAYER available */
163
164 if ((NULL != bottom)
165 && (_PR_FILEDESC_OPEN == bottom->secret->state))
166 {
167 if (0 == ready)
168 {
169 osfd = (SOCKET) bottom->secret->md.osfd;
170 if (in_flags_read & PR_POLL_READ)
171 {
172 pd->out_flags |= _PR_POLL_READ_SYS_READ;
173 FD_SET(osfd, &rd);
174 nrd++;
175 }
176 if (in_flags_read & PR_POLL_WRITE)
177 {
178 pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
179 FD_SET(osfd, &wt);
180 nwt++;
181 }
182 if (in_flags_write & PR_POLL_READ)
183 {
184 pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
185 FD_SET(osfd, &rd);
186 nrd++;
187 }
188 if (in_flags_write & PR_POLL_WRITE)
189 {
190 pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
191 FD_SET(osfd, &wt);
192 nwt++;
193 }
194 if (pd->in_flags & PR_POLL_EXCEPT) {
195 FD_SET(osfd, &ex);
196 nex++;
197 }
198 }
199 }
200 else
201 {
202 if (0 == ready)
203 {
204 PRPollDesc *prev;
205 for (prev = pds; prev < pd; prev++)
206 {
207 prev->out_flags = 0;
208 }
209 }
210 ready += 1; /* this will cause an abrupt return */
211 pd->out_flags = PR_POLL_NVAL; /* bogii */
212 }
213 }
214 }
215 else
216 {
217 pd->out_flags = 0;
218 }
219 }
220
221 if (0 != ready) {
222 return ready; /* no need to block */
223 }
224
225 /*
226 * FD_SET does nothing if the fd_set's internal fd_array is full. If
227 * nrd, nwt, or nex is greater than FD_SETSIZE, we know FD_SET must
228 * have failed to insert an osfd into the corresponding fd_set, and
229 * therefore we should fail.
230 */
231 if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) {
232 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
233 return -1;
234 }
235
236 rdp = (0 == nrd) ? NULL : &rd;
237 wtp = (0 == nwt) ? NULL : &wt;
238 exp = (0 == nex) ? NULL : &ex;
239
240 if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) {
241 PR_Sleep(timeout);
242 return 0;
243 }
244
245 if (timeout != PR_INTERVAL_NO_TIMEOUT)
246 {
247 PRInt32 ticksPerSecond = PR_TicksPerSecond();
248 tv.tv_sec = timeout / ticksPerSecond;
249 tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond );
250 tvp = &tv;
251 }
252
253 #if defined(_PR_GLOBAL_THREADS_ONLY)
254 ready = _MD_SELECT(0, rdp, wtp, exp, tvp);
255 #else
256 ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp);
257 #endif
258
259 /*
260 ** Now to unravel the select sets back into the client's poll
261 ** descriptor list. Is this possibly an area for pissing away
262 ** a few cycles or what?
263 */
264 if (ready > 0)
265 {
266 ready = 0;
267 for (pd = pds, epd = pd + npds; pd < epd; pd++)
268 {
269 PRInt16 out_flags = 0;
270 if ((NULL != pd->fd) && (0 != pd->in_flags))
271 {
272 SOCKET osfd;
273 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
274 PR_ASSERT(NULL != bottom);
275
276 osfd = (SOCKET) bottom->secret->md.osfd;
277
278 if (FD_ISSET(osfd, &rd))
279 {
280 if (pd->out_flags & _PR_POLL_READ_SYS_READ) {
281 out_flags |= PR_POLL_READ;
282 }
283 if (pd->out_flags & _PR_POLL_WRITE_SYS_READ) {
284 out_flags |= PR_POLL_WRITE;
285 }
286 }
287 if (FD_ISSET(osfd, &wt))
288 {
289 if (pd->out_flags & _PR_POLL_READ_SYS_WRITE) {
290 out_flags |= PR_POLL_READ;
291 }
292 if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE) {
293 out_flags |= PR_POLL_WRITE;
294 }
295 }
296 if (FD_ISSET(osfd, &ex)) {
297 out_flags |= PR_POLL_EXCEPT;
298 }
299 }
300 pd->out_flags = out_flags;
301 if (out_flags) {
302 ready++;
303 }
304 }
305 PR_ASSERT(ready > 0);
306 }
307 else if (ready == SOCKET_ERROR)
308 {
309 err = WSAGetLastError();
310 if (err == WSAENOTSOCK)
311 {
312 /* Find the bad fds */
313 int optval;
314 int optlen = sizeof(optval);
315 ready = 0;
316 for (pd = pds, epd = pd + npds; pd < epd; pd++)
317 {
318 pd->out_flags = 0;
319 if ((NULL != pd->fd) && (0 != pd->in_flags))
320 {
321 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
322 if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
323 SO_TYPE, (char *) &optval, &optlen) == -1)
324 {
325 PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
326 if (WSAGetLastError() == WSAENOTSOCK)
327 {
328 pd->out_flags = PR_POLL_NVAL;
329 ready++;
330 }
331 }
332 }
333 }
334 PR_ASSERT(ready > 0);
335 }
336 else {
337 _PR_MD_MAP_SELECT_ERROR(err);
338 }
339 }
340
341 return ready;
342 }
343