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