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