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