Back to index

lightning-sunbird  0.9+nobinonly
uxwrap.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  *------------------------------------------------------------------------
00040  * File: uxwrap.c
00041  *
00042  *     Our wrapped versions of the Unix select() and poll() system calls.
00043  *
00044  *------------------------------------------------------------------------
00045  */
00046 
00047 #include "primpl.h"
00048 
00049 #if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX)
00050 /* Do not wrap select() and poll(). */
00051 #else  /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
00052 /* The include files for select() */
00053 #ifdef IRIX
00054 #include <unistd.h>
00055 #include <bstring.h>
00056 #endif
00057 
00058 #include <string.h>
00059 #include <sys/types.h>
00060 #include <sys/time.h>
00061 
00062 #define ZAP_SET(_to, _width)                                  \
00063     PR_BEGIN_MACRO                                     \
00064        memset(_to, 0,                                         \
00065               ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \
00066               * sizeof(int)                            \
00067               );                                       \
00068     PR_END_MACRO
00069 
00070 /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */
00071 static int _pr_xt_hack_fd = -1;
00072  
00073 int PR_XGetXtHackFD(void)
00074 {
00075     int fds[2];
00076  
00077     if (_pr_xt_hack_fd == -1) {
00078         if (!pipe(fds)) {
00079             _pr_xt_hack_fd = fds[0];
00080         }
00081     }
00082     return _pr_xt_hack_fd;
00083 }
00084 
00085 static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0;
00086 
00087 void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void))
00088 {
00089     _pr_xt_hack_okayToReleaseXLock = fn; 
00090 }
00091 
00092 
00093 /*
00094  *-----------------------------------------------------------------------
00095  *  select() --
00096  *
00097  *    Wrap up the select system call so that we can deschedule
00098  *    a thread that tries to wait for i/o.
00099  *
00100  *-----------------------------------------------------------------------
00101  */
00102 
00103 #if defined(HPUX9)
00104 int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv)
00105 #elif defined(NEXTSTEP)
00106 int wrap_select(int width, fd_set *rd, fd_set *wr, fd_set *ex,
00107         const struct timeval *tv)
00108 #elif defined(AIX_RENAME_SELECT)
00109 int wrap_select(unsigned long width, void *rl, void *wl, void *el,
00110         struct timeval *tv)
00111 #elif defined(_PR_SELECT_CONST_TIMEVAL)
00112 int select(int width, fd_set *rd, fd_set *wr, fd_set *ex,
00113         const struct timeval *tv)
00114 #else
00115 int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv)
00116 #endif
00117 {
00118     int osfd;
00119     _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
00120     PRInt32 pdcnt;
00121     PRIntervalTime timeout;
00122     int retVal;
00123 #if defined(HPUX9) || defined(AIX_RENAME_SELECT)
00124     fd_set *rd = (fd_set*) rl;
00125     fd_set *wr = (fd_set*) wl;
00126     fd_set *ex = (fd_set*) el;
00127 #endif
00128 
00129 #if 0
00130     /*
00131      * Easy special case: zero timeout.  Simply call the native
00132      * select() with no fear of blocking.
00133      */
00134     if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) {
00135 #if defined(HPUX9) || defined(AIX_RENAME_SELECT)
00136         return _MD_SELECT(width, rl, wl, el, tv);
00137 #else
00138         return _MD_SELECT(width, rd, wr, ex, tv);
00139 #endif
00140     }
00141 #endif
00142 
00143     if (!_pr_initialized) {
00144         _PR_ImplicitInitialization();
00145     }
00146               
00147 #ifndef _PR_LOCAL_THREADS_ONLY
00148     if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
00149         return _MD_SELECT(width, rd, wr, ex, tv);       
00150     }
00151 #endif
00152 
00153     if (width < 0 || width > FD_SETSIZE) {
00154         errno = EINVAL;
00155         return -1;
00156     }
00157 
00158     /* Compute timeout */
00159     if (tv) {
00160         /*
00161          * These acceptable ranges for t_sec and t_usec are taken
00162          * from the select() man pages.
00163          */
00164         if (tv->tv_sec < 0 || tv->tv_sec > 100000000
00165                 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
00166             errno = EINVAL;
00167             return -1;
00168         }
00169 
00170         /* Convert microseconds to ticks */
00171         timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec);
00172     } else {
00173         /* tv being a NULL pointer means blocking indefinitely */
00174         timeout = PR_INTERVAL_NO_TIMEOUT;
00175     }
00176 
00177     /* Check for no descriptors case (just doing a timeout) */
00178     if ((!rd && !wr && !ex) || !width) {
00179         PR_Sleep(timeout);
00180         return 0;
00181     }
00182 
00183     /*
00184      * Set up for PR_Poll().  The PRPollDesc array is allocated
00185      * dynamically.  If this turns out to have high performance
00186      * penalty, one can change to use a large PRPollDesc array
00187      * on the stack, and allocate dynamically only when it turns
00188      * out to be not large enough.
00189      *
00190      * I allocate an array of size 'width', which is the maximum
00191      * number of fds we may need to poll.
00192      */
00193     unixpds = (_PRUnixPollDesc *) PR_CALLOC(width * sizeof(_PRUnixPollDesc));
00194     if (!unixpds) {
00195         errno = ENOMEM;
00196         return -1;
00197     }
00198 
00199     pdcnt = 0;
00200     unixpd = unixpds;
00201     for (osfd = 0; osfd < width; osfd++) {
00202         int in_flags = 0;
00203         if (rd && FD_ISSET(osfd, rd)) {
00204             in_flags |= _PR_UNIX_POLL_READ;
00205         }
00206         if (wr && FD_ISSET(osfd, wr)) {
00207             in_flags |= _PR_UNIX_POLL_WRITE;
00208         }
00209         if (ex && FD_ISSET(osfd, ex)) {
00210             in_flags |= _PR_UNIX_POLL_EXCEPT;
00211         }
00212         if (in_flags) {
00213             unixpd->osfd = osfd;
00214             unixpd->in_flags = in_flags;
00215             unixpd->out_flags = 0;
00216             unixpd++;
00217             pdcnt++;
00218         }
00219     }
00220 
00221     /*
00222      * see comments in mozilla/cmd/xfe/mozilla.c (look for
00223      * "PR_XGetXtHackFD")
00224      */
00225    {
00226      int needToLockXAgain;
00227  
00228      needToLockXAgain = 0;
00229      if (rd && (_pr_xt_hack_fd != -1)
00230              && FD_ISSET(_pr_xt_hack_fd, rd) && PR_XIsLocked()
00231              && (!_pr_xt_hack_okayToReleaseXLock
00232              || _pr_xt_hack_okayToReleaseXLock())) {
00233          PR_XUnlock();
00234          needToLockXAgain = 1;
00235      }
00236 
00237     /* This is the potentially blocking step */
00238     retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout);
00239 
00240      if (needToLockXAgain) {
00241          PR_XLock();
00242      }
00243    }
00244 
00245     if (retVal > 0) {
00246         /* Compute select results */
00247         if (rd) ZAP_SET(rd, width);
00248         if (wr) ZAP_SET(wr, width);
00249         if (ex) ZAP_SET(ex, width);
00250 
00251         /*
00252          * The return value can be either the number of ready file
00253          * descriptors or the number of set bits in the three fd_set's.
00254          */
00255         retVal = 0;  /* we're going to recompute */
00256         eunixpd = unixpds + pdcnt;
00257         for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
00258             if (unixpd->out_flags) {
00259                 int nbits = 0;  /* The number of set bits on for this fd */
00260 
00261                 if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
00262                     errno = EBADF;
00263                     PR_LOG(_pr_io_lm, PR_LOG_ERROR,
00264                             ("select returns EBADF for %d", unixpd->osfd));
00265                     retVal = -1;
00266                     break;
00267                 }
00268                 /*
00269                  * If a socket has a pending error, it is considered
00270                  * both readable and writable.  (See W. Richard Stevens,
00271                  * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3,
00272                  * pp. 153-154.)  We also consider a socket readable if
00273                  * it has a hangup condition.
00274                  */
00275                 if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ)
00276                         && (unixpd->out_flags & (_PR_UNIX_POLL_READ
00277                         | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) {
00278                     FD_SET(unixpd->osfd, rd);
00279                     nbits++;
00280                 }
00281                 if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
00282                         && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE
00283                         | _PR_UNIX_POLL_ERR))) {
00284                     FD_SET(unixpd->osfd, wr);
00285                     nbits++;
00286                 }
00287                 if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE)
00288                         && (unixpd->out_flags & PR_POLL_EXCEPT)) {
00289                     FD_SET(unixpd->osfd, ex);
00290                     nbits++;
00291                 }
00292                 PR_ASSERT(nbits > 0);
00293 #if defined(HPUX) || defined(SOLARIS) || defined(SUNOS4) || defined(OSF1) || defined(AIX)
00294                 retVal += nbits;
00295 #else /* IRIX */
00296                 retVal += 1;
00297 #endif
00298             }
00299         }
00300     }
00301 
00302     PR_ASSERT(tv || retVal != 0);
00303     PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal));
00304     PR_DELETE(unixpds);
00305 
00306     return retVal;
00307 }
00308 
00309 /*
00310  * Redefine poll, when supported on platforms, for local threads
00311  */
00312 
00313 /*
00314  * I am commenting out the poll() wrapper for Linux for now
00315  * because it is difficult to define _MD_POLL that works on all
00316  * Linux varieties.  People reported that glibc 2.0.7 on Debian
00317  * 2.0 Linux machines doesn't have the __syscall_poll symbol
00318  * defined.  (WTC 30 Nov. 1998)
00319  */
00320 #if defined(_PR_POLL_AVAILABLE) && !defined(LINUX)
00321 
00322 /*
00323  *-----------------------------------------------------------------------
00324  * poll() --
00325  *
00326  * RETURN VALUES: 
00327  *     -1:  fails, errno indicates the error.
00328  *      0:  timed out, the revents bitmasks are not set.
00329  *      positive value: the number of file descriptors for which poll()
00330  *          has set the revents bitmask.
00331  *
00332  *-----------------------------------------------------------------------
00333  */
00334 
00335 #include <poll.h>
00336 
00337 #if defined(AIX_RENAME_SELECT)
00338 int wrap_poll(void *listptr, unsigned long nfds, long timeout)
00339 #elif (defined(AIX) && !defined(AIX_RENAME_SELECT))
00340 int poll(void *listptr, unsigned long nfds, long timeout)
00341 #elif defined(OSF1) || (defined(HPUX) && !defined(HPUX9))
00342 int poll(struct pollfd filedes[], unsigned int nfds, int timeout)
00343 #elif defined(HPUX9)
00344 int poll(struct pollfd filedes[], int nfds, int timeout)
00345 #elif defined(NETBSD)
00346 int poll(struct pollfd *filedes, nfds_t nfds, int timeout)
00347 #elif defined(OPENBSD)
00348 int poll(struct pollfd filedes[], nfds_t nfds, int timeout)
00349 #elif defined(FREEBSD)
00350 int poll(struct pollfd *filedes, unsigned nfds, int timeout)
00351 #else
00352 int poll(struct pollfd *filedes, unsigned long nfds, int timeout)
00353 #endif
00354 {
00355 #ifdef AIX
00356     struct pollfd *filedes = (struct pollfd *) listptr;
00357 #endif
00358     struct pollfd *pfd, *epfd;
00359     _PRUnixPollDesc *unixpds, *unixpd, *eunixpd;
00360     PRIntervalTime ticks;
00361     PRInt32 pdcnt;
00362     int ready;
00363 
00364     /*
00365      * Easy special case: zero timeout.  Simply call the native
00366      * poll() with no fear of blocking.
00367      */
00368     if (timeout == 0) {
00369 #if defined(AIX)
00370         return _MD_POLL(listptr, nfds, timeout);
00371 #else
00372         return _MD_POLL(filedes, nfds, timeout);
00373 #endif
00374     }
00375 
00376     if (!_pr_initialized) {
00377         _PR_ImplicitInitialization();
00378     }
00379 
00380 #ifndef _PR_LOCAL_THREADS_ONLY
00381     if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) {
00382        return _MD_POLL(filedes, nfds, timeout);
00383     }
00384 #endif
00385 
00386     /* We do not support the pollmsg structures on AIX */
00387 #ifdef AIX
00388     PR_ASSERT((nfds & 0xff00) == 0);
00389 #endif
00390 
00391     if (timeout < 0 && timeout != -1) {
00392         errno = EINVAL;
00393         return -1;
00394     }
00395 
00396     /* Convert timeout from miliseconds to ticks */
00397     if (timeout == -1) {
00398         ticks = PR_INTERVAL_NO_TIMEOUT;
00399     } else {
00400         ticks = PR_MillisecondsToInterval(timeout);
00401     }
00402 
00403     /* Check for no descriptor case (just do a timeout) */
00404     if (nfds == 0) {
00405         PR_Sleep(ticks);
00406         return 0;
00407     }
00408 
00409     unixpds = (_PRUnixPollDesc *)
00410             PR_MALLOC(nfds * sizeof(_PRUnixPollDesc));
00411     if (NULL == unixpds) {
00412         errno = EAGAIN;
00413         return -1;
00414     }
00415 
00416     pdcnt = 0;
00417     epfd = filedes + nfds;
00418     unixpd = unixpds;
00419     for (pfd = filedes; pfd < epfd; pfd++) {
00420         /*
00421          * poll() ignores negative fd's.
00422          */
00423         if (pfd->fd >= 0) {
00424             unixpd->osfd = pfd->fd;
00425 #ifdef _PR_USE_POLL
00426             unixpd->in_flags = pfd->events;
00427 #else
00428             /*
00429              * Map the poll events to one of the three that can be
00430              * represented by the select fd_sets:
00431              *     POLLIN, POLLRDNORM  ===> readable
00432              *     POLLOUT, POLLWRNORM ===> writable
00433              *     POLLPRI, POLLRDBAND ===> exception
00434              *     POLLNORM, POLLWRBAND (and POLLMSG on some platforms)
00435              *     are ignored.
00436              *
00437              * The output events POLLERR and POLLHUP are never turned on.
00438              * POLLNVAL may be turned on.
00439              */
00440             unixpd->in_flags = 0;
00441             if (pfd->events & (POLLIN
00442 #ifdef POLLRDNORM
00443                     | POLLRDNORM
00444 #endif
00445                     )) {
00446                 unixpd->in_flags |= _PR_UNIX_POLL_READ;
00447             }
00448             if (pfd->events & (POLLOUT
00449 #ifdef POLLWRNORM
00450                     | POLLWRNORM
00451 #endif
00452                     )) {
00453                 unixpd->in_flags |= _PR_UNIX_POLL_WRITE;
00454             }
00455             if (pfd->events & (POLLPRI
00456 #ifdef POLLRDBAND
00457                     | POLLRDBAND
00458 #endif
00459                     )) {
00460                 unixpd->in_flags |= PR_POLL_EXCEPT;
00461             }
00462 #endif  /* _PR_USE_POLL */
00463             unixpd->out_flags = 0;
00464             unixpd++;
00465             pdcnt++;
00466         }
00467     }
00468 
00469     ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks);
00470     if (-1 == ready) {
00471         if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) {
00472             errno = EINTR;  /* XXX we aren't interrupted by a signal, but... */
00473         } else {
00474             errno = PR_GetOSError();
00475         }
00476     }
00477     if (ready <= 0) {
00478         goto done;
00479     }
00480 
00481     /*
00482      * Copy the out_flags from the _PRUnixPollDesc structures to the
00483      * user's pollfd structures and free the allocated memory
00484      */
00485     unixpd = unixpds;
00486     for (pfd = filedes; pfd < epfd; pfd++) {
00487         pfd->revents = 0;
00488         if (pfd->fd >= 0) {
00489 #ifdef _PR_USE_POLL
00490             pfd->revents = unixpd->out_flags;
00491 #else
00492             if (0 != unixpd->out_flags) {
00493                 if (unixpd->out_flags & _PR_UNIX_POLL_READ) {
00494                     if (pfd->events & POLLIN) {
00495                         pfd->revents |= POLLIN;
00496                     }
00497 #ifdef POLLRDNORM
00498                     if (pfd->events & POLLRDNORM) {
00499                         pfd->revents |= POLLRDNORM;
00500                     }
00501 #endif
00502                 }
00503                 if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) {
00504                     if (pfd->events & POLLOUT) {
00505                         pfd->revents |= POLLOUT;
00506                     }
00507 #ifdef POLLWRNORM
00508                     if (pfd->events & POLLWRNORM) {
00509                         pfd->revents |= POLLWRNORM;
00510                     }
00511 #endif
00512                 }
00513                 if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) {
00514                     if (pfd->events & POLLPRI) {
00515                         pfd->revents |= POLLPRI;
00516                     }
00517 #ifdef POLLRDBAND
00518                     if (pfd->events & POLLRDBAND) {
00519                         pfd->revents |= POLLRDBAND;
00520                     }
00521 #endif
00522                 }
00523                 if (unixpd->out_flags & _PR_UNIX_POLL_ERR) {
00524                     pfd->revents |= POLLERR;
00525                 }
00526                 if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) {
00527                     pfd->revents |= POLLNVAL;
00528                 }
00529                 if (unixpd->out_flags & _PR_UNIX_POLL_HUP) {
00530                     pfd->revents |= POLLHUP;
00531                 }
00532             }
00533 #endif  /* _PR_USE_POLL */
00534             unixpd++;
00535         }
00536     }
00537 
00538 done:
00539     PR_DELETE(unixpds);
00540     return ready;
00541 }
00542 
00543 #endif  /* !defined(LINUX) */
00544 
00545 #endif  /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */
00546 
00547 /* uxwrap.c */
00548