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