Back to index

lightning-sunbird  0.9+nobinonly
w32poll.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * This file implements _PR_MD_PR_POLL for Win32.
00040  */
00041 
00042 /* The default value of FD_SETSIZE is 64. */
00043 #define FD_SETSIZE 1024
00044 
00045 #include "primpl.h"
00046 
00047 #if !defined(_PR_GLOBAL_THREADS_ONLY)
00048 
00049 struct select_data_s {
00050     PRInt32 status;
00051     PRInt32 error;
00052     fd_set *rd, *wt, *ex;
00053     const struct timeval *tv;
00054 };
00055 
00056 static void
00057 _PR_MD_select_thread(void *cdata)
00058 {
00059     struct select_data_s *cd = (struct select_data_s *)cdata;
00060 
00061     cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
00062 
00063     if (cd->status == SOCKET_ERROR) {
00064         cd->error = WSAGetLastError();
00065     }
00066 }
00067 
00068 int _PR_NTFiberSafeSelect(
00069     int nfds,
00070     fd_set *readfds,
00071     fd_set *writefds,
00072     fd_set *exceptfds,
00073     const struct timeval *timeout)
00074 {
00075     PRThread *me = _PR_MD_CURRENT_THREAD();
00076     int ready;
00077 
00078     if (_PR_IS_NATIVE_THREAD(me)) {
00079         ready = _MD_SELECT(nfds, readfds, writefds, exceptfds, timeout);
00080     }
00081     else
00082     {
00083         /*
00084         ** Creating a new thread on each call!!
00085         ** I guess web server doesn't use non-block I/O.
00086         */
00087         PRThread *selectThread;
00088         struct select_data_s data;
00089         data.status = 0;
00090         data.error = 0;
00091         data.rd = readfds;
00092         data.wt = writefds;
00093         data.ex = exceptfds;
00094         data.tv = timeout;
00095 
00096         selectThread = PR_CreateThread(
00097             PR_USER_THREAD, _PR_MD_select_thread, &data,
00098             PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
00099         if (selectThread == NULL) return -1;
00100 
00101         PR_JoinThread(selectThread);
00102         ready = data.status;
00103         if (ready == SOCKET_ERROR) WSASetLastError(data.error);
00104     }
00105     return ready;
00106 }
00107 
00108 #endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
00109 
00110 PRInt32 _PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
00111 {
00112     int ready, err;
00113     fd_set rd, wt, ex;
00114     fd_set *rdp, *wtp, *exp;
00115     int nrd, nwt, nex;
00116     PRFileDesc *bottom;
00117     PRPollDesc *pd, *epd;
00118     PRThread *me = _PR_MD_CURRENT_THREAD();
00119 
00120     struct timeval tv, *tvp = NULL;
00121 
00122     if (_PR_PENDING_INTERRUPT(me))
00123     {
00124         me->flags &= ~_PR_INTERRUPT;
00125         PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
00126         return -1;
00127     }
00128 
00129     /*
00130     ** Is it an empty set? If so, just sleep for the timeout and return
00131     */
00132     if (0 == npds)
00133     {
00134         PR_Sleep(timeout);
00135         return 0;
00136     }
00137 
00138     nrd = nwt = nex = 0;
00139     FD_ZERO(&rd);
00140     FD_ZERO(&wt);
00141     FD_ZERO(&ex);
00142 
00143     ready = 0;
00144     for (pd = pds, epd = pd + npds; pd < epd; pd++)
00145     {
00146         SOCKET osfd;
00147         PRInt16 in_flags_read = 0, in_flags_write = 0;
00148         PRInt16 out_flags_read = 0, out_flags_write = 0;
00149 
00150         if ((NULL != pd->fd) && (0 != pd->in_flags))
00151         {
00152             if (pd->in_flags & PR_POLL_READ)
00153             {
00154                 in_flags_read = (pd->fd->methods->poll)(
00155                     pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_WRITE),
00156                     &out_flags_read);
00157             }
00158             if (pd->in_flags & PR_POLL_WRITE)
00159             {
00160                 in_flags_write = (pd->fd->methods->poll)(
00161                     pd->fd, (PRInt16)(pd->in_flags & ~PR_POLL_READ),
00162                     &out_flags_write);
00163             }
00164             if ((0 != (in_flags_read & out_flags_read))
00165             || (0 != (in_flags_write & out_flags_write)))
00166             {
00167                 /* this one's ready right now (buffered input) */
00168                 if (0 == ready)
00169                 {
00170                     /*
00171                      * We will have to return without calling the
00172                      * system poll/select function.  So zero the
00173                      * out_flags fields of all the poll descriptors
00174                      * before this one.
00175                      */
00176                     PRPollDesc *prev;
00177                     for (prev = pds; prev < pd; prev++)
00178                     {
00179                         prev->out_flags = 0;
00180                     }
00181                 }
00182                 ready += 1;
00183                 pd->out_flags = out_flags_read | out_flags_write;
00184             }
00185             else
00186             {
00187                 pd->out_flags = 0;  /* pre-condition */
00188                 /* make sure this is an NSPR supported stack */
00189                 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
00190                 PR_ASSERT(NULL != bottom);  /* what to do about that? */
00191                 if ((NULL != bottom)
00192                 && (_PR_FILEDESC_OPEN == bottom->secret->state))
00193                 {
00194                     if (0 == ready)
00195                     {
00196                         osfd = (SOCKET) bottom->secret->md.osfd;
00197                         if (in_flags_read & PR_POLL_READ)
00198                         {
00199                             pd->out_flags |= _PR_POLL_READ_SYS_READ;
00200                             FD_SET(osfd, &rd);
00201                             nrd++;
00202                         }
00203                         if (in_flags_read & PR_POLL_WRITE)
00204                         {
00205                             pd->out_flags |= _PR_POLL_READ_SYS_WRITE;
00206                             FD_SET(osfd, &wt);
00207                             nwt++;
00208                         }
00209                         if (in_flags_write & PR_POLL_READ)
00210                         {
00211                             pd->out_flags |= _PR_POLL_WRITE_SYS_READ;
00212                             FD_SET(osfd, &rd);
00213                             nrd++;
00214                         }
00215                         if (in_flags_write & PR_POLL_WRITE)
00216                         {
00217                             pd->out_flags |= _PR_POLL_WRITE_SYS_WRITE;
00218                             FD_SET(osfd, &wt);
00219                             nwt++;
00220                         }
00221                         if (pd->in_flags & PR_POLL_EXCEPT) {
00222                             FD_SET(osfd, &ex);
00223                             nex++;
00224                         }
00225                     }
00226                 }
00227                 else
00228                 {
00229                     if (0 == ready)
00230                     {
00231                         PRPollDesc *prev;
00232                         for (prev = pds; prev < pd; prev++)
00233                         {
00234                             prev->out_flags = 0;
00235                         }
00236                     }
00237                     ready += 1;  /* this will cause an abrupt return */
00238                     pd->out_flags = PR_POLL_NVAL;  /* bogii */
00239                 }
00240             }
00241         }
00242         else
00243         {
00244             pd->out_flags = 0;
00245         }
00246     }
00247 
00248     if (0 != ready) return ready;  /* no need to block */
00249 
00250     if ((nrd > FD_SETSIZE) || (nwt > FD_SETSIZE) || (nex > FD_SETSIZE)) {
00251         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
00252         return -1;
00253     }
00254 
00255     rdp = (0 == nrd) ? NULL : &rd;
00256     wtp = (0 == nwt) ? NULL : &wt;
00257     exp = (0 == nex) ? NULL : &ex;
00258 
00259     if ((NULL == rdp) && (NULL == wtp) && (NULL == exp)) {
00260         PR_Sleep(timeout);
00261         return 0;
00262     }
00263 
00264     if (timeout != PR_INTERVAL_NO_TIMEOUT)
00265     {
00266         PRInt32 ticksPerSecond = PR_TicksPerSecond();
00267         tv.tv_sec = timeout / ticksPerSecond;
00268         tv.tv_usec = PR_IntervalToMicroseconds( timeout % ticksPerSecond );
00269         tvp = &tv;
00270     }
00271 
00272 #if defined(_PR_GLOBAL_THREADS_ONLY)
00273     ready = _MD_SELECT(0, rdp, wtp, exp, tvp);
00274 #else
00275     ready = _PR_NTFiberSafeSelect(0, rdp, wtp, exp, tvp);
00276 #endif
00277 
00278     /*
00279     ** Now to unravel the select sets back into the client's poll
00280     ** descriptor list. Is this possibly an area for pissing away
00281     ** a few cycles or what?
00282     */
00283     if (ready > 0)
00284     {
00285         ready = 0;
00286         for (pd = pds, epd = pd + npds; pd < epd; pd++)
00287         {
00288             PRInt16 out_flags = 0;
00289             if ((NULL != pd->fd) && (0 != pd->in_flags))
00290             {
00291                 SOCKET osfd;
00292                 bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
00293                 PR_ASSERT(NULL != bottom);
00294 
00295                 osfd = (SOCKET) bottom->secret->md.osfd;
00296 
00297                 if (FD_ISSET(osfd, &rd))
00298                 {
00299                     if (pd->out_flags & _PR_POLL_READ_SYS_READ)
00300                         out_flags |= PR_POLL_READ;
00301                     if (pd->out_flags & _PR_POLL_WRITE_SYS_READ)
00302                         out_flags |= PR_POLL_WRITE;
00303                 } 
00304                 if (FD_ISSET(osfd, &wt))
00305                 {
00306                     if (pd->out_flags & _PR_POLL_READ_SYS_WRITE)
00307                         out_flags |= PR_POLL_READ;
00308                     if (pd->out_flags & _PR_POLL_WRITE_SYS_WRITE)
00309                         out_flags |= PR_POLL_WRITE;
00310                 } 
00311                 if (FD_ISSET(osfd, &ex)) out_flags |= PR_POLL_EXCEPT;
00312             }
00313             pd->out_flags = out_flags;
00314             if (out_flags) ready++;
00315         }
00316         PR_ASSERT(ready > 0);
00317     }
00318     else if (ready == SOCKET_ERROR)
00319     {
00320         err = WSAGetLastError();
00321         if (err == WSAENOTSOCK)
00322         {
00323             /* Find the bad fds */
00324             int optval;
00325             int optlen = sizeof(optval);
00326             ready = 0;
00327             for (pd = pds, epd = pd + npds; pd < epd; pd++)
00328             {
00329                 pd->out_flags = 0;
00330                 if ((NULL != pd->fd) && (0 != pd->in_flags))
00331                 {
00332                     bottom = PR_GetIdentitiesLayer(pd->fd, PR_NSPR_IO_LAYER);
00333                     if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
00334                         SO_TYPE, (char *) &optval, &optlen) == -1)
00335                     {
00336                         PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
00337                         if (WSAGetLastError() == WSAENOTSOCK)
00338                         {
00339                             pd->out_flags = PR_POLL_NVAL;
00340                             ready++;
00341                         }
00342                     }
00343                 }
00344             }
00345             PR_ASSERT(ready > 0);
00346         }
00347         else _PR_MD_MAP_SELECT_ERROR(err);
00348     }
00349 
00350     return ready;
00351 }