nsprpub/pr/src/md/unix/uxwrap.c (339 lines of code) (raw):

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* *------------------------------------------------------------------------ * File: uxwrap.c * * Our wrapped versions of the Unix select() and poll() system calls. * *------------------------------------------------------------------------ */ #include "primpl.h" #if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) || defined(QNX) /* Do not wrap select() and poll(). */ #else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ /* The include files for select() */ # include <string.h> # include <sys/types.h> # include <sys/time.h> # define ZAP_SET(_to, _width) \ PR_BEGIN_MACRO \ memset( \ _to, 0, \ ((_width + 8 * sizeof(int) - 1) / (8 * sizeof(int))) * sizeof(int)); \ PR_END_MACRO /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */ static int _pr_xt_hack_fd = -1; int PR_XGetXtHackFD(void) { int fds[2]; if (_pr_xt_hack_fd == -1) { if (!pipe(fds)) { _pr_xt_hack_fd = fds[0]; } } return _pr_xt_hack_fd; } static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0; void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void)) { _pr_xt_hack_okayToReleaseXLock = fn; } /* *----------------------------------------------------------------------- * select() -- * * Wrap up the select system call so that we can deschedule * a thread that tries to wait for i/o. * *----------------------------------------------------------------------- */ # if defined(AIX_RENAME_SELECT) int wrap_select(unsigned long width, void* rl, void* wl, void* el, struct timeval* tv) # elif defined(_PR_SELECT_CONST_TIMEVAL) int select(int width, fd_set* rd, fd_set* wr, fd_set* ex, const struct timeval* tv) # else int select(int width, fd_set* rd, fd_set* wr, fd_set* ex, struct timeval* tv) # endif { int osfd; _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; PRInt32 pdcnt; PRIntervalTime timeout; int retVal; # if defined(AIX_RENAME_SELECT) fd_set* rd = (fd_set*)rl; fd_set* wr = (fd_set*)wl; fd_set* ex = (fd_set*)el; # endif # if 0 /* * Easy special case: zero timeout. Simply call the native * select() with no fear of blocking. */ if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) { # if defined(AIX_RENAME_SELECT) return _MD_SELECT(width, rl, wl, el, tv); # else return _MD_SELECT(width, rd, wr, ex, tv); # endif } # endif if (!_pr_initialized) { _PR_ImplicitInitialization(); } # ifndef _PR_LOCAL_THREADS_ONLY if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { return _MD_SELECT(width, rd, wr, ex, tv); } # endif if (width < 0 || width > FD_SETSIZE) { errno = EINVAL; return -1; } /* Compute timeout */ if (tv) { /* * These acceptable ranges for t_sec and t_usec are taken * from the select() man pages. */ if (tv->tv_sec < 0 || tv->tv_sec > 100000000 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) { errno = EINVAL; return -1; } /* Convert microseconds to ticks */ timeout = PR_MicrosecondsToInterval(1000000 * tv->tv_sec + tv->tv_usec); } else { /* tv being a NULL pointer means blocking indefinitely */ timeout = PR_INTERVAL_NO_TIMEOUT; } /* Check for no descriptors case (just doing a timeout) */ if ((!rd && !wr && !ex) || !width) { PR_Sleep(timeout); return 0; } /* * Set up for PR_Poll(). The PRPollDesc array is allocated * dynamically. If this turns out to have high performance * penalty, one can change to use a large PRPollDesc array * on the stack, and allocate dynamically only when it turns * out to be not large enough. * * I allocate an array of size 'width', which is the maximum * number of fds we may need to poll. */ unixpds = (_PRUnixPollDesc*)PR_CALLOC(width * sizeof(_PRUnixPollDesc)); if (!unixpds) { errno = ENOMEM; return -1; } pdcnt = 0; unixpd = unixpds; for (osfd = 0; osfd < width; osfd++) { int in_flags = 0; if (rd && FD_ISSET(osfd, rd)) { in_flags |= _PR_UNIX_POLL_READ; } if (wr && FD_ISSET(osfd, wr)) { in_flags |= _PR_UNIX_POLL_WRITE; } if (ex && FD_ISSET(osfd, ex)) { in_flags |= _PR_UNIX_POLL_EXCEPT; } if (in_flags) { unixpd->osfd = osfd; unixpd->in_flags = in_flags; unixpd->out_flags = 0; unixpd++; pdcnt++; } } /* * see comments in mozilla/cmd/xfe/mozilla.c (look for * "PR_XGetXtHackFD") */ { int needToLockXAgain; needToLockXAgain = 0; if (rd && (_pr_xt_hack_fd != -1) && FD_ISSET(_pr_xt_hack_fd, rd) && _PR_XIsLocked() && (!_pr_xt_hack_okayToReleaseXLock || _pr_xt_hack_okayToReleaseXLock())) { _PR_XUnlock(); needToLockXAgain = 1; } /* This is the potentially blocking step */ retVal = _PR_WaitForMultipleFDs(unixpds, pdcnt, timeout); if (needToLockXAgain) { _PR_XLock(); } } if (retVal > 0) { /* Compute select results */ if (rd) { ZAP_SET(rd, width); } if (wr) { ZAP_SET(wr, width); } if (ex) { ZAP_SET(ex, width); } /* * The return value can be either the number of ready file * descriptors or the number of set bits in the three fd_set's. */ retVal = 0; /* we're going to recompute */ eunixpd = unixpds + pdcnt; for (unixpd = unixpds; unixpd < eunixpd; unixpd++) { if (unixpd->out_flags) { int nbits = 0; /* The number of set bits on for this fd */ if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { errno = EBADF; PR_LOG(_pr_io_lm, PR_LOG_ERROR, ("select returns EBADF for %d", unixpd->osfd)); retVal = -1; break; } /* * If a socket has a pending error, it is considered * both readable and writable. (See W. Richard Stevens, * Unix Network Programming, Vol. 1, 2nd Ed., Section 6.3, * pp. 153-154.) We also consider a socket readable if * it has a hangup condition. */ if (rd && (unixpd->in_flags & _PR_UNIX_POLL_READ) && (unixpd->out_flags & (_PR_UNIX_POLL_READ | _PR_UNIX_POLL_ERR | _PR_UNIX_POLL_HUP))) { FD_SET(unixpd->osfd, rd); nbits++; } if (wr && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) && (unixpd->out_flags & (_PR_UNIX_POLL_WRITE | _PR_UNIX_POLL_ERR))) { FD_SET(unixpd->osfd, wr); nbits++; } if (ex && (unixpd->in_flags & _PR_UNIX_POLL_WRITE) && (unixpd->out_flags & PR_POLL_EXCEPT)) { FD_SET(unixpd->osfd, ex); nbits++; } PR_ASSERT(nbits > 0); # if defined(HPUX) || defined(SOLARIS) || defined(AIX) retVal += nbits; # endif } } } PR_ASSERT(tv || retVal != 0); PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal)); PR_DELETE(unixpds); return retVal; } /* * Redefine poll, when supported on platforms, for local threads */ /* * I am commenting out the poll() wrapper for Linux for now * because it is difficult to define _MD_POLL that works on all * Linux varieties. People reported that glibc 2.0.7 on Debian * 2.0 Linux machines doesn't have the __syscall_poll symbol * defined. (WTC 30 Nov. 1998) */ # if defined(_PR_POLL_AVAILABLE) && !defined(LINUX) /* *----------------------------------------------------------------------- * poll() -- * * RETURN VALUES: * -1: fails, errno indicates the error. * 0: timed out, the revents bitmasks are not set. * positive value: the number of file descriptors for which poll() * has set the revents bitmask. * *----------------------------------------------------------------------- */ # include <poll.h> # if defined(AIX_RENAME_SELECT) int wrap_poll(void* listptr, unsigned long nfds, long timeout) # elif (defined(AIX) && !defined(AIX_RENAME_SELECT)) int poll(void* listptr, unsigned long nfds, long timeout) # elif defined(HPUX) int poll(struct pollfd filedes[], unsigned int nfds, int timeout) # elif defined(NETBSD) int poll(struct pollfd* filedes, nfds_t nfds, int timeout) # elif defined(OPENBSD) int poll(struct pollfd filedes[], nfds_t nfds, int timeout) # elif defined(FREEBSD) int poll(struct pollfd* filedes, unsigned nfds, int timeout) # else int poll(struct pollfd* filedes, unsigned long nfds, int timeout) # endif { # ifdef AIX struct pollfd* filedes = (struct pollfd*)listptr; # endif struct pollfd *pfd, *epfd; _PRUnixPollDesc *unixpds, *unixpd, *eunixpd; PRIntervalTime ticks; PRInt32 pdcnt; int ready; /* * Easy special case: zero timeout. Simply call the native * poll() with no fear of blocking. */ if (timeout == 0) { # if defined(AIX) return _MD_POLL(listptr, nfds, timeout); # else return _MD_POLL(filedes, nfds, timeout); # endif } if (!_pr_initialized) { _PR_ImplicitInitialization(); } # ifndef _PR_LOCAL_THREADS_ONLY if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { return _MD_POLL(filedes, nfds, timeout); } # endif /* We do not support the pollmsg structures on AIX */ # ifdef AIX PR_ASSERT((nfds & 0xff00) == 0); # endif if (timeout < 0 && timeout != -1) { errno = EINVAL; return -1; } /* Convert timeout from miliseconds to ticks */ if (timeout == -1) { ticks = PR_INTERVAL_NO_TIMEOUT; } else { ticks = PR_MillisecondsToInterval(timeout); } /* Check for no descriptor case (just do a timeout) */ if (nfds == 0) { PR_Sleep(ticks); return 0; } unixpds = (_PRUnixPollDesc*)PR_MALLOC(nfds * sizeof(_PRUnixPollDesc)); if (NULL == unixpds) { errno = EAGAIN; return -1; } pdcnt = 0; epfd = filedes + nfds; unixpd = unixpds; for (pfd = filedes; pfd < epfd; pfd++) { /* * poll() ignores negative fd's. */ if (pfd->fd >= 0) { unixpd->osfd = pfd->fd; # ifdef _PR_USE_POLL unixpd->in_flags = pfd->events; # else /* * Map the poll events to one of the three that can be * represented by the select fd_sets: * POLLIN, POLLRDNORM ===> readable * POLLOUT, POLLWRNORM ===> writable * POLLPRI, POLLRDBAND ===> exception * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) * are ignored. * * The output events POLLERR and POLLHUP are never turned on. * POLLNVAL may be turned on. */ unixpd->in_flags = 0; if (pfd->events & (POLLIN # ifdef POLLRDNORM | POLLRDNORM # endif )) { unixpd->in_flags |= _PR_UNIX_POLL_READ; } if (pfd->events & (POLLOUT # ifdef POLLWRNORM | POLLWRNORM # endif )) { unixpd->in_flags |= _PR_UNIX_POLL_WRITE; } if (pfd->events & (POLLPRI # ifdef POLLRDBAND | POLLRDBAND # endif )) { unixpd->in_flags |= PR_POLL_EXCEPT; } # endif /* _PR_USE_POLL */ unixpd->out_flags = 0; unixpd++; pdcnt++; } } ready = _PR_WaitForMultipleFDs(unixpds, pdcnt, ticks); if (-1 == ready) { if (PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { errno = EINTR; /* XXX we aren't interrupted by a signal, but... */ } else { errno = PR_GetOSError(); } } if (ready <= 0) { goto done; } /* * Copy the out_flags from the _PRUnixPollDesc structures to the * user's pollfd structures and free the allocated memory */ unixpd = unixpds; for (pfd = filedes; pfd < epfd; pfd++) { pfd->revents = 0; if (pfd->fd >= 0) { # ifdef _PR_USE_POLL pfd->revents = unixpd->out_flags; # else if (0 != unixpd->out_flags) { if (unixpd->out_flags & _PR_UNIX_POLL_READ) { if (pfd->events & POLLIN) { pfd->revents |= POLLIN; } # ifdef POLLRDNORM if (pfd->events & POLLRDNORM) { pfd->revents |= POLLRDNORM; } # endif } if (unixpd->out_flags & _PR_UNIX_POLL_WRITE) { if (pfd->events & POLLOUT) { pfd->revents |= POLLOUT; } # ifdef POLLWRNORM if (pfd->events & POLLWRNORM) { pfd->revents |= POLLWRNORM; } # endif } if (unixpd->out_flags & _PR_UNIX_POLL_EXCEPT) { if (pfd->events & POLLPRI) { pfd->revents |= POLLPRI; } # ifdef POLLRDBAND if (pfd->events & POLLRDBAND) { pfd->revents |= POLLRDBAND; } # endif } if (unixpd->out_flags & _PR_UNIX_POLL_ERR) { pfd->revents |= POLLERR; } if (unixpd->out_flags & _PR_UNIX_POLL_NVAL) { pfd->revents |= POLLNVAL; } if (unixpd->out_flags & _PR_UNIX_POLL_HUP) { pfd->revents |= POLLHUP; } } # endif /* _PR_USE_POLL */ unixpd++; } } done: PR_DELETE(unixpds); return ready; } # endif /* !defined(LINUX) */ #endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ /* uxwrap.c */