Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/nsprpub/pr/src/md/unix/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 91 kB image not shown  

Quelle  unix.c

  Sprache: C
 

/* -*- 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/. */


#include "primpl.h"

#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/utsname.h>

#ifdef _PR_POLL_AVAILABLE
#  include <poll.h>
#endif

#if defined(ANDROID)
#  include <android/api-level.h>
#endif

#if defined(NTO)
#  include <sys/statvfs.h>
#endif

/*
 * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or
 * PRInt32* pointer to a _PRSockLen_t* pointer.
 */

#if defined(HAVE_SOCKLEN_T) || (defined(__GLIBC__) && __GLIBC__ >= 2)
#  define _PRSockLen_t socklen_t
#elif defined(HPUX) || defined(SOLARIS) || defined(AIX4_1) || \
    defined(LINUX) || defined(DARWIN) || defined(QNX)
#  define _PRSockLen_t int
#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) || \
    defined(NETBSD) || defined(OPENBSD)
|| defined(NTO) ||
    defined(RISCOS)
#  define _PRSockLen_t size_t
#else
#  error "Cannot determine architecture"
#endif

/*
** Global lock variable used to bracket calls into rusty libraries that
** aren't thread safe (like libc, libX, etc).
*/

static PRLock* _pr_unix_rename_lock = NULL;
static PRMonitor* _pr_Xfe_mon = NULL;

static PRInt64 minus_one;

sigset_t timer_set;

#if !defined(_PR_PTHREADS)

static sigset_t empty_set;

#  ifdef SOLARIS
#    include <sys/file.h>
#    include <sys/filio.h>
#  endif

#  ifndef PIPE_BUF
#    define PIPE_BUF 512
#  endif

/*
 * _nspr_noclock - if set clock interrupts are disabled
 */

int _nspr_noclock = 1;

/*
 * There is an assertion in this code that NSPR's definition of PRIOVec
 * is bit compatible with UNIX' definition of a struct iovec. This is
 * applicable to the 'writev()' operations where the types are casually
 * cast to avoid warnings.
 */


int _pr_md_pipefd[2] = {-1, -1};
static char _pr_md_pipebuf[PIPE_BUF];
static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag,
                             PRIntervalTime timeout);

_PRInterruptTable _pr_interruptTable[] = {{
                                              "clock",
                                              _PR_MISSED_CLOCK,
                                              _PR_ClockInterrupt,
                                          },
                                          {0}};

void _MD_unix_init_running_cpu(_PRCPU* cpu) {
  PR_INIT_CLIST(&(cpu->md.md_unix.ioQ));
  cpu->md.md_unix.ioq_max_osfd = -1;
  cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
}

PRStatus _MD_open_dir(_MDDir* d, const char* name) {
  int err;

  d->d = opendir(name);
  if (!d->d) {
    err = _MD_ERRNO();
    _PR_MD_MAP_OPENDIR_ERROR(err);
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

PRInt32 _MD_close_dir(_MDDir* d) {
  int rv = 0, err;

  if (d->d) {
    rv = closedir(d->d);
    if (rv == -1) {
      err = _MD_ERRNO();
      _PR_MD_MAP_CLOSEDIR_ERROR(err);
    }
  }
  return rv;
}

char* _MD_read_dir(_MDDir* d, PRIntn flags) {
  struct dirent* de;
  int err;

  for (;;) {
    /*
     * XXX: readdir() is not MT-safe. There is an MT-safe version
     * readdir_r() on some systems.
     */

    _MD_ERRNO() = 0;
    de = readdir(d->d);
    if (!de) {
      err = _MD_ERRNO();
      _PR_MD_MAP_READDIR_ERROR(err);
      return 0;
    }
    if ((flags & PR_SKIP_DOT) && (de->d_name[0] == '.') &&
        (de->d_name[1] == 0)) {
      continue;
    }
    if ((flags & PR_SKIP_DOT_DOT) && (de->d_name[0] == '.') &&
        (de->d_name[1] == '.') && (de->d_name[2] == 0)) {
      continue;
    }
    if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.')) {
      continue;
    }
    break;
  }
  return de->d_name;
}

PRInt32 _MD_delete(const char* name) {
  PRInt32 rv, err;

  rv = unlink(name);
  if (rv == -1) {
    err = _MD_ERRNO();
    _PR_MD_MAP_UNLINK_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_rename(const char* from, const char* to) {
  PRInt32 rv = -1, err;

  /*
  ** This is trying to enforce the semantics of WINDOZE' rename
  ** operation. That means one is not allowed to rename over top
  ** of an existing file. Holding a lock across these two function
  ** and the open function is known to be a bad idea, but ....
  */

  if (NULL != _pr_unix_rename_lock) {
    PR_Lock(_pr_unix_rename_lock);
  }
  if (0 == access(to, F_OK)) {
    PR_SetError(PR_FILE_EXISTS_ERROR, 0);
  } else {
    rv = rename(from, to);
    if (rv < 0) {
      err = _MD_ERRNO();
      _PR_MD_MAP_RENAME_ERROR(err);
    }
  }
  if (NULL != _pr_unix_rename_lock) {
    PR_Unlock(_pr_unix_rename_lock);
  }
  return rv;
}

PRInt32 _MD_access(const char* name, PRAccessHow how) {
  PRInt32 rv, err;
  int amode;

  switch (how) {
    case PR_ACCESS_WRITE_OK:
      amode = W_OK;
      break;
    case PR_ACCESS_READ_OK:
      amode = R_OK;
      break;
    case PR_ACCESS_EXISTS:
      amode = F_OK;
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      rv = -1;
      goto done;
  }
  rv = access(name, amode);

  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_ACCESS_ERROR(err);
  }

done:
  return (rv);
}

PRInt32 _MD_mkdir(const char* name, PRIntn mode) {
  int rv, err;

  /*
  ** This lock is used to enforce rename semantics as described
  ** in PR_Rename. Look there for more fun details.
  */

  if (NULL != _pr_unix_rename_lock) {
    PR_Lock(_pr_unix_rename_lock);
  }
  rv = mkdir(name, mode);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_MKDIR_ERROR(err);
  }
  if (NULL != _pr_unix_rename_lock) {
    PR_Unlock(_pr_unix_rename_lock);
  }
  return rv;
}

PRInt32 _MD_rmdir(const char* name) {
  int rv, err;

  rv = rmdir(name);
  if (rv == -1) {
    err = _MD_ERRNO();
    _PR_MD_MAP_RMDIR_ERROR(err);
  }
  return rv;
}

PRInt32 _MD_read(PRFileDesc* fd, void* buf, PRInt32 amount) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRInt32 rv, err;
#  ifndef _PR_USE_POLL
  fd_set rd;
#  else
  struct pollfd pfd;
#  endif /* _PR_USE_POLL */
  PRInt32 osfd = fd->secret->md.osfd;

#  ifndef _PR_USE_POLL
  FD_ZERO(&rd);
  FD_SET(osfd, &rd);
#  else
  pfd.fd = osfd;
  pfd.events = POLLIN;
#  endif /* _PR_USE_POLL */
  while ((rv = read(osfd, buf, amount)) == -1) {
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ,
                                PR_INTERVAL_NO_TIMEOUT)) < 0) {
          goto done;
        }
      } else {
#  ifndef _PR_USE_POLL
        while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL)) == -1 &&
               (err = _MD_ERRNO()) == EINTR) {
          /* retry _MD_SELECT() if it is interrupted */
        }
#  else  /* _PR_USE_POLL */
        while ((rv = _MD_POLL(&pfd, 1, -1)) == -1 &&
               (err = _MD_ERRNO()) == EINTR) {
          /* retry _MD_POLL() if it is interrupted */
        }
#  endif /* _PR_USE_POLL */
        if (rv == -1) {
          break;
        }
      }
      if (_PR_PENDING_INTERRUPT(me)) {
        break;
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    } else {
      _PR_MD_MAP_READ_ERROR(err);
    }
  }
done:
  return (rv);
}

PRInt32 _MD_write(PRFileDesc* fd, const void* buf, PRInt32 amount) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRInt32 rv, err;
#  ifndef _PR_USE_POLL
  fd_set wd;
#  else
  struct pollfd pfd;
#  endif /* _PR_USE_POLL */
  PRInt32 osfd = fd->secret->md.osfd;

#  ifndef _PR_USE_POLL
  FD_ZERO(&wd);
  FD_SET(osfd, &wd);
#  else
  pfd.fd = osfd;
  pfd.events = POLLOUT;
#  endif /* _PR_USE_POLL */
  while ((rv = write(osfd, buf, amount)) == -1) {
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE,
                                PR_INTERVAL_NO_TIMEOUT)) < 0) {
          goto done;
        }
      } else {
#  ifndef _PR_USE_POLL
        while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL)) == -1 &&
               (err = _MD_ERRNO()) == EINTR) {
          /* retry _MD_SELECT() if it is interrupted */
        }
#  else  /* _PR_USE_POLL */
        while ((rv = _MD_POLL(&pfd, 1, -1)) == -1 &&
               (err = _MD_ERRNO()) == EINTR) {
          /* retry _MD_POLL() if it is interrupted */
        }
#  endif /* _PR_USE_POLL */
        if (rv == -1) {
          break;
        }
      }
      if (_PR_PENDING_INTERRUPT(me)) {
        break;
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    } else {
      _PR_MD_MAP_WRITE_ERROR(err);
    }
  }
done:
  return (rv);
}

PRInt32 _MD_fsync(PRFileDesc* fd) {
  PRInt32 rv, err;

  rv = fsync(fd->secret->md.osfd);
  if (rv == -1) {
    err = _MD_ERRNO();
    _PR_MD_MAP_FSYNC_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_close(PRInt32 osfd) {
  PRInt32 rv, err;

  rv = close(osfd);
  if (rv == -1) {
    err = _MD_ERRNO();
    _PR_MD_MAP_CLOSE_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto) {
  PRInt32 osfd, err;

  osfd = socket(domain, type, proto);

  if (osfd == -1) {
    err = _MD_ERRNO();
    _PR_MD_MAP_SOCKET_ERROR(err);
    return (osfd);
  }

  return (osfd);
}

PRInt32 _MD_socketavailable(PRFileDesc* fd) {
  PRInt32 result;

  if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) {
    _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO());
    return -1;
  }
  return result;
}

PRInt64 _MD_socketavailable64(PRFileDesc* fd) {
  PRInt64 result;
  LL_I2L(result, _MD_socketavailable(fd));
  return result;
/* _MD_socketavailable64 */

#  define READ_FD 1
#  define WRITE_FD 2

/*
 * socket_io_wait --
 *
 * wait for socket i/o, periodically checking for interrupt
 *
 * The first implementation uses select(), for platforms without
 * poll().  The second (preferred) implementation uses poll().
 */


#  ifndef _PR_USE_POLL

static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
                              PRIntervalTime timeout) {
  PRInt32 rv = -1;
  struct timeval tv;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRIntervalTime epoch, now, elapsed, remaining;
  PRBool wait_for_remaining;
  PRInt32 syserror;
  fd_set rd_wr;

  switch (timeout) {
    case PR_INTERVAL_NO_WAIT:
      PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
      break;
    case PR_INTERVAL_NO_TIMEOUT:
      /*
       * This is a special case of the 'default' case below.
       * Please see the comments there.
       */

      tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
      tv.tv_usec = 0;
      FD_ZERO(&rd_wr);
      do {
        FD_SET(osfd, &rd_wr);
        if (fd_type == READ_FD) {
          rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
        } else {
          rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
        }
        if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
          _PR_MD_MAP_SELECT_ERROR(syserror);
          break;
        }
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          rv = -1;
          break;
        }
      } while (rv == 0 || (rv == -1 && syserror == EINTR));
      break;
    default:
      now = epoch = PR_IntervalNow();
      remaining = timeout;
      FD_ZERO(&rd_wr);
      do {
        /*
         * We block in _MD_SELECT for at most
         * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
         * so that there is an upper limit on the delay
         * before the interrupt bit is checked.
         */

        wait_for_remaining = PR_TRUE;
        tv.tv_sec = PR_IntervalToSeconds(remaining);
        if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) {
          wait_for_remaining = PR_FALSE;
          tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS;
          tv.tv_usec = 0;
        } else {
          tv.tv_usec = PR_IntervalToMicroseconds(
              remaining - PR_SecondsToInterval(tv.tv_sec));
        }
        FD_SET(osfd, &rd_wr);
        if (fd_type == READ_FD) {
          rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, &tv);
        } else {
          rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, &tv);
        }
        /*
         * we don't consider EINTR a real error
         */

        if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
          _PR_MD_MAP_SELECT_ERROR(syserror);
          break;
        }
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          rv = -1;
          break;
        }
        /*
         * We loop again if _MD_SELECT timed out or got interrupted
         * by a signal, and the timeout deadline has not passed yet.
         */

        if (rv == 0 || (rv == -1 && syserror == EINTR)) {
          /*
           * If _MD_SELECT timed out, we know how much time
           * we spent in blocking, so we can avoid a
           * PR_IntervalNow() call.
           */

          if (rv == 0) {
            if (wait_for_remaining) {
              now += remaining;
            } else {
              now += PR_SecondsToInterval(tv.tv_sec) +
                     PR_MicrosecondsToInterval(tv.tv_usec);
            }
          } else {
            now = PR_IntervalNow();
          }
          elapsed = (PRIntervalTime)(now - epoch);
          if (elapsed >= timeout) {
            PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
            rv = -1;
            break;
          } else {
            remaining = timeout - elapsed;
          }
        }
      } while (rv == 0 || (rv == -1 && syserror == EINTR));
      break;
  }
  return (rv);
}

#  else /* _PR_USE_POLL */

static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type,
                              PRIntervalTime timeout) {
  PRInt32 rv = -1;
  int msecs;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRIntervalTime epoch, now, elapsed, remaining;
  PRBool wait_for_remaining;
  PRInt32 syserror;
  struct pollfd pfd;

  switch (timeout) {
    case PR_INTERVAL_NO_WAIT:
      PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
      break;
    case PR_INTERVAL_NO_TIMEOUT:
      /*
       * This is a special case of the 'default' case below.
       * Please see the comments there.
       */

      msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
      pfd.fd = osfd;
      if (fd_type == READ_FD) {
        pfd.events = POLLIN;
      } else {
        pfd.events = POLLOUT;
      }
      do {
        rv = _MD_POLL(&pfd, 1, msecs);
        if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
          _PR_MD_MAP_POLL_ERROR(syserror);
          break;
        }
        /*
         * If POLLERR is set, don't process it; retry the operation
         */

        if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
          rv = -1;
          _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
          break;
        }
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          rv = -1;
          break;
        }
      } while (rv == 0 || (rv == -1 && syserror == EINTR));
      break;
    default:
      now = epoch = PR_IntervalNow();
      remaining = timeout;
      pfd.fd = osfd;
      if (fd_type == READ_FD) {
        pfd.events = POLLIN;
      } else {
        pfd.events = POLLOUT;
      }
      do {
        /*
         * We block in _MD_POLL for at most
         * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds,
         * so that there is an upper limit on the delay
         * before the interrupt bit is checked.
         */

        wait_for_remaining = PR_TRUE;
        msecs = PR_IntervalToMilliseconds(remaining);
        if (msecs > _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000) {
          wait_for_remaining = PR_FALSE;
          msecs = _PR_INTERRUPT_CHECK_INTERVAL_SECS * 1000;
        }
        rv = _MD_POLL(&pfd, 1, msecs);
        /*
         * we don't consider EINTR a real error
         */

        if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) {
          _PR_MD_MAP_POLL_ERROR(syserror);
          break;
        }
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          rv = -1;
          break;
        }
        /*
         * If POLLERR is set, don't process it; retry the operation
         */

        if ((rv == 1) && (pfd.revents & (POLLHUP | POLLNVAL))) {
          rv = -1;
          _PR_MD_MAP_POLL_REVENTS_ERROR(pfd.revents);
          break;
        }
        /*
         * We loop again if _MD_POLL timed out or got interrupted
         * by a signal, and the timeout deadline has not passed yet.
         */

        if (rv == 0 || (rv == -1 && syserror == EINTR)) {
          /*
           * If _MD_POLL timed out, we know how much time
           * we spent in blocking, so we can avoid a
           * PR_IntervalNow() call.
           */

          if (rv == 0) {
            if (wait_for_remaining) {
              now += remaining;
            } else {
              now += PR_MillisecondsToInterval(msecs);
            }
          } else {
            now = PR_IntervalNow();
          }
          elapsed = (PRIntervalTime)(now - epoch);
          if (elapsed >= timeout) {
            PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
            rv = -1;
            break;
          } else {
            remaining = timeout - elapsed;
          }
        }
      } while (rv == 0 || (rv == -1 && syserror == EINTR));
      break;
  }
  return (rv);
}

#  endif /* _PR_USE_POLL */

static PRInt32 local_io_wait(PRInt32 osfd, PRInt32 wait_flag,
                             PRIntervalTime timeout) {
  _PRUnixPollDesc pd;
  PRInt32 rv;

  PR_LOG(_pr_io_lm, PR_LOG_MIN,
         ("waiting to %s on osfd=%d",
          (wait_flag == _PR_UNIX_POLL_READ) ? "read" : "write", osfd));

  if (timeout == PR_INTERVAL_NO_WAIT) {
    return 0;
  }

  pd.osfd = osfd;
  pd.in_flags = wait_flag;
  pd.out_flags = 0;

  rv = _PR_WaitForMultipleFDs(&pd, 1, timeout);

  if (rv == 0) {
    PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
    rv = -1;
  }
  return rv;
}

PRInt32 _MD_recv(PRFileDesc* fd, void* buf, PRInt32 amount, PRInt32 flags,
                 PRIntervalTime timeout) {
  PRInt32 osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  /*
   * Many OS's (ex: Solaris) have a broken recv which won't read
   * from socketpairs.  As long as we don't use flags on socketpairs, this
   * is a decent fix. - mikep
   */

#  if defined(SOLARIS)
  while ((rv = read(osfd, buf, amount)) == -1) {
#  else
  while ((rv = recv(osfd, buf, amount, flags)) == -1) {
#  endif
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_RECV_ERROR(err);
  }
done:
  return (rv);
}

PRInt32 _MD_recvfrom(PRFileDesc* fd, void* buf, PRInt32 amount, PRIntn flags,
                     PRNetAddr* addr, PRUint32* addrlen,
                     PRIntervalTime timeout) {
  PRInt32 osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  while ((*addrlen = PR_NETADDR_SIZE(addr)),
         ((rv = recvfrom(osfd, buf, amount, flags, (struct sockaddr*)addr,
                         (_PRSockLen_t*)addrlen)) == -1)) {
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_RECVFROM_ERROR(err);
  }
done:
#  ifdef _PR_HAVE_SOCKADDR_LEN
  if (rv != -1) {
    /* ignore the sa_len field of struct sockaddr */
    if (addr) {
      addr->raw.family = ((struct sockaddr*)addr)->sa_family;
    }
  }
#  endif /* _PR_HAVE_SOCKADDR_LEN */
  return (rv);
}

PRInt32 _MD_send(PRFileDesc* fd, const void* buf, PRInt32 amount, PRInt32 flags,
                 PRIntervalTime timeout) {
  PRInt32 osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();
#  if defined(SOLARIS)
  PRInt32 tmp_amount = amount;
#  endif

  /*
   * On pre-2.6 Solaris, send() is much slower than write().
   * On 2.6 and beyond, with in-kernel sockets, send() and
   * write() are fairly equivalent in performance.
   */

#  if defined(SOLARIS)
  PR_ASSERT(0 == flags);
  while ((rv = write(osfd, buf, tmp_amount)) == -1) {
#  else
  while ((rv = send(osfd, buf, amount, flags)) == -1) {
#  endif
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
#  if defined(SOLARIS)
      /*
       * The write system call has been reported to return the ERANGE
       * error on occasion. Try to write in smaller chunks to workaround
       * this bug.
       */

      if (err == ERANGE) {
        if (tmp_amount > 1) {
          tmp_amount = tmp_amount / 2; /* half the bytes */
          continue;
        }
      }
#  endif
      break;
    }
  }
  /*
   * optimization; if bytes sent is less than "amount" call
   * select before returning. This is because it is likely that
   * the next send() call will return EWOULDBLOCK.
   */

  if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) &&
      (timeout != PR_INTERVAL_NO_WAIT)) {
    if (_PR_IS_NATIVE_THREAD(me)) {
      if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) {
        rv = -1;
        goto done;
      }
    } else {
      if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
        rv = -1;
        goto done;
      }
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_SEND_ERROR(err);
  }
done:
  return (rv);
}

PRInt32 _MD_sendto(PRFileDesc* fd, const void* buf, PRInt32 amount,
                   PRIntn flags, const PRNetAddr* addr, PRUint32 addrlen,
                   PRIntervalTime timeout) {
  PRInt32 osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();
#  ifdef _PR_HAVE_SOCKADDR_LEN
  PRNetAddr addrCopy;

  addrCopy = *addr;
  ((struct sockaddr*)&addrCopy)->sa_len = addrlen;
  ((struct sockaddr*)&addrCopy)->sa_family = addr->raw.family;

  while ((rv = sendto(osfd, buf, amount, flags, (struct sockaddr*)&addrCopy,
                      addrlen)) == -1) {
#  else
  while ((rv = sendto(osfd, buf, amount, flags, (struct sockaddr*)addr,
                      addrlen)) == -1) {
#  endif
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_SENDTO_ERROR(err);
  }
done:
  return (rv);
}

PRInt32 _MD_writev(PRFileDesc* fd, const PRIOVec* iov, PRInt32 iov_size,
                   PRIntervalTime timeout) {
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRInt32 index, amount = 0;
  PRInt32 osfd = fd->secret->md.osfd;

  /*
   * Calculate the total number of bytes to be sent; needed for
   * optimization later.
   * We could avoid this if this number was passed in; but it is
   * probably not a big deal because iov_size is usually small (less than
   * 3)
   */

  if (!fd->secret->nonblocking) {
    for (index = 0; index < iov_size; index++) {
      amount += iov[index].iov_len;
    }
  }

  while ((rv = writev(osfd, (const struct iovec*)iov, iov_size)) == -1) {
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, WRITE_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  /*
   * optimization; if bytes sent is less than "amount" call
   * select before returning. This is because it is likely that
   * the next writev() call will return EWOULDBLOCK.
   */

  if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) &&
      (timeout != PR_INTERVAL_NO_WAIT)) {
    if (_PR_IS_NATIVE_THREAD(me)) {
      if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) {
        rv = -1;
        goto done;
      }
    } else {
      if (local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout) < 0) {
        rv = -1;
        goto done;
      }
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_WRITEV_ERROR(err);
  }
done:
  return (rv);
}

PRInt32 _MD_accept(PRFileDesc* fd, PRNetAddr* addr, PRUint32* addrlen,
                   PRIntervalTime timeout) {
  PRInt32 osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  while ((rv = accept(osfd, (struct sockaddr*)addr, (_PRSockLen_t*)addrlen)) ==
         -1) {
    err = _MD_ERRNO();
    if ((err == EAGAIN) || (err == EWOULDBLOCK) || (err == ECONNABORTED)) {
      if (fd->secret->nonblocking) {
        break;
      }
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_READ, timeout)) < 0) {
          goto done;
        }
      } else {
        if ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) {
          goto done;
        }
      }
    } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))) {
      continue;
    } else {
      break;
    }
  }
  if (rv < 0) {
    _PR_MD_MAP_ACCEPT_ERROR(err);
  }
done:
#  ifdef _PR_HAVE_SOCKADDR_LEN
  if (rv != -1) {
    /* ignore the sa_len field of struct sockaddr */
    if (addr) {
      addr->raw.family = ((struct sockaddr*)addr)->sa_family;
    }
  }
#  endif /* _PR_HAVE_SOCKADDR_LEN */
  return (rv);
}

extern int _connect(int s, const struct sockaddr* name, int namelen);
PRInt32 _MD_connect(PRFileDesc* fd, const PRNetAddr* addr, PRUint32 addrlen,
                    PRIntervalTime timeout) {
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRInt32 osfd = fd->secret->md.osfd;
#  ifdef _PR_HAVE_SOCKADDR_LEN
  PRNetAddr addrCopy;

  addrCopy = *addr;
  ((struct sockaddr*)&addrCopy)->sa_len = addrlen;
  ((struct sockaddr*)&addrCopy)->sa_family = addr->raw.family;
#  endif

  /*
   * We initiate the connection setup by making a nonblocking connect()
   * call.  If the connect() call fails, there are two cases we handle
   * specially:
   * 1. The connect() call was interrupted by a signal.  In this case
   *    we simply retry connect().
   * 2. The NSPR socket is nonblocking and connect() fails with
   *    EINPROGRESS.  We first wait until the socket becomes writable.
   *    Then we try to find out whether the connection setup succeeded
   *    or failed.
   */


retry:
#  ifdef _PR_HAVE_SOCKADDR_LEN
  if ((rv = connect(osfd, (struct sockaddr*)&addrCopy, addrlen)) == -1) {
#  else
  if ((rv = connect(osfd, (struct sockaddr*)addr, addrlen)) == -1) {
#  endif
    err = _MD_ERRNO();

    if (err == EINTR) {
      if (_PR_PENDING_INTERRUPT(me)) {
        me->flags &= ~_PR_INTERRUPT;
        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
        return -1;
      }
      goto retry;
    }

    if (!fd->secret->nonblocking && (err == EINPROGRESS)) {
      if (!_PR_IS_NATIVE_THREAD(me)) {
        if ((rv = local_io_wait(osfd, _PR_UNIX_POLL_WRITE, timeout)) < 0) {
          return -1;
        }
      } else {
        /*
         * socket_io_wait() may return -1 or 1.
         */


        rv = socket_io_wait(osfd, WRITE_FD, timeout);
        if (rv == -1) {
          return -1;
        }
      }

      PR_ASSERT(rv == 1);
      if (_PR_PENDING_INTERRUPT(me)) {
        me->flags &= ~_PR_INTERRUPT;
        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
        return -1;
      }
      err = _MD_unix_get_nonblocking_connect_error(osfd);
      if (err != 0) {
        _PR_MD_MAP_CONNECT_ERROR(err);
        return -1;
      }
      return 0;
    }

    _PR_MD_MAP_CONNECT_ERROR(err);
  }

  return rv;
/* _MD_connect */

PRInt32 _MD_bind(PRFileDesc* fd, const PRNetAddr* addr, PRUint32 addrlen) {
  PRInt32 rv, err;
#  ifdef _PR_HAVE_SOCKADDR_LEN
  PRNetAddr addrCopy;

  addrCopy = *addr;
  ((struct sockaddr*)&addrCopy)->sa_len = addrlen;
  ((struct sockaddr*)&addrCopy)->sa_family = addr->raw.family;
  rv = bind(fd->secret->md.osfd, (struct sockaddr*)&addrCopy, (int)addrlen);
#  else
  rv = bind(fd->secret->md.osfd, (struct sockaddr*)addr, (int)addrlen);
#  endif
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_BIND_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_listen(PRFileDesc* fd, PRIntn backlog) {
  PRInt32 rv, err;

  rv = listen(fd->secret->md.osfd, backlog);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_LISTEN_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_shutdown(PRFileDesc* fd, PRIntn how) {
  PRInt32 rv, err;

  rv = shutdown(fd->secret->md.osfd, how);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_SHUTDOWN_ERROR(err);
  }
  return (rv);
}

PRInt32 _MD_socketpair(int af, int type, int flags, PRInt32* osfd) {
  PRInt32 rv, err;

  rv = socketpair(af, type, flags, osfd);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_SOCKETPAIR_ERROR(err);
  }
  return rv;
}

PRStatus _MD_getsockname(PRFileDesc* fd, PRNetAddr* addr, PRUint32* addrlen) {
  PRInt32 rv, err;

  rv = getsockname(fd->secret->md.osfd, (struct sockaddr*)addr,
                   (_PRSockLen_t*)addrlen);
#  ifdef _PR_HAVE_SOCKADDR_LEN
  if (rv == 0) {
    /* ignore the sa_len field of struct sockaddr */
    if (addr) {
      addr->raw.family = ((struct sockaddr*)addr)->sa_family;
    }
  }
#  endif /* _PR_HAVE_SOCKADDR_LEN */
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_GETSOCKNAME_ERROR(err);
  }
  return rv == 0 ? PR_SUCCESS : PR_FAILURE;
}

PRStatus _MD_getpeername(PRFileDesc* fd, PRNetAddr* addr, PRUint32* addrlen) {
  PRInt32 rv, err;

  rv = getpeername(fd->secret->md.osfd, (struct sockaddr*)addr,
                   (_PRSockLen_t*)addrlen);
#  ifdef _PR_HAVE_SOCKADDR_LEN
  if (rv == 0) {
    /* ignore the sa_len field of struct sockaddr */
    if (addr) {
      addr->raw.family = ((struct sockaddr*)addr)->sa_family;
    }
  }
#  endif /* _PR_HAVE_SOCKADDR_LEN */
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_GETPEERNAME_ERROR(err);
  }
  return rv == 0 ? PR_SUCCESS : PR_FAILURE;
}

PRStatus _MD_getsockopt(PRFileDesc* fd, PRInt32 level, PRInt32 optname,
                        char* optval, PRInt32* optlen) {
  PRInt32 rv, err;

  rv = getsockopt(fd->secret->md.osfd, level, optname, optval,
                  (_PRSockLen_t*)optlen);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_GETSOCKOPT_ERROR(err);
  }
  return rv == 0 ? PR_SUCCESS : PR_FAILURE;
}

PRStatus _MD_setsockopt(PRFileDesc* fd, PRInt32 level, PRInt32 optname,
                        const char* optval, PRInt32 optlen) {
  PRInt32 rv, err;

  rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen);
  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_SETSOCKOPT_ERROR(err);
  }
  return rv == 0 ? PR_SUCCESS : PR_FAILURE;
}

PRStatus _MD_set_fd_inheritable(PRFileDesc* fd, PRBool inheritable) {
  int rv;

  rv = fcntl(fd->secret->md.osfd, F_SETFD, inheritable ? 0 : FD_CLOEXEC);
  if (-1 == rv) {
    PR_SetError(PR_UNKNOWN_ERROR, _MD_ERRNO());
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

void _MD_init_fd_inheritable(PRFileDesc* fd, PRBool imported) {
  if (imported) {
    fd->secret->inheritable = _PR_TRI_UNKNOWN;
  } else {
    /* By default, a Unix fd is not closed on exec. */
#  ifdef DEBUG
    {
      int flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
      PR_ASSERT(0 == flags);
    }
#  endif
    fd->secret->inheritable = _PR_TRI_TRUE;
  }
}

/************************************************************************/
#  if !defined(_PR_USE_POLL)

/*
** Scan through io queue and find any bad fd's that triggered the error
** from _MD_SELECT
*/

static void FindBadFDs(void) {
  PRCList* q;
  PRThread* me = _MD_CURRENT_THREAD();

  PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
  q = (_PR_IOQ(me->cpu)).next;
  _PR_IOQ_MAX_OSFD(me->cpu) = -1;
  _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
  while (q != &_PR_IOQ(me->cpu)) {
    PRPollQueue* pq = _PR_POLLQUEUE_PTR(q);
    PRBool notify = PR_FALSE;
    _PRUnixPollDesc* pds = pq->pds;
    _PRUnixPollDesc* epds = pds + pq->npds;
    PRInt32 pq_max_osfd = -1;

    q = q->next;
    for (; pds < epds; pds++) {
      PRInt32 osfd = pds->osfd;
      pds->out_flags = 0;
      PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
      if (pds->in_flags == 0) {
        continue/* skip this fd */
      }
      if (fcntl(osfd, F_GETFL, 0) == -1) {
        /* Found a bad descriptor, remove it from the fd_sets. */
        PR_LOG(_pr_io_lm, PR_LOG_MAX, ("file descriptor %d is bad", osfd));
        pds->out_flags = _PR_UNIX_POLL_NVAL;
        notify = PR_TRUE;
      }
      if (osfd > pq_max_osfd) {
        pq_max_osfd = osfd;
      }
    }

    if (notify) {
      PRIntn pri;
      PR_REMOVE_LINK(&pq->links);
      pq->on_ioq = PR_FALSE;

      /*
       * Decrement the count of descriptors for each desciptor/event
       * because this I/O request is being removed from the
       * ioq
       */

      pds = pq->pds;
      for (; pds < epds; pds++) {
        PRInt32 osfd = pds->osfd;
        PRInt16 in_flags = pds->in_flags;
        PR_ASSERT(osfd >= 0 || in_flags == 0);
        if (in_flags & _PR_UNIX_POLL_READ) {
          if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
          }
        }
        if (in_flags & _PR_UNIX_POLL_WRITE) {
          if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
          }
        }
        if (in_flags & _PR_UNIX_POLL_EXCEPT) {
          if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
          }
        }
      }

      _PR_THREAD_LOCK(pq->thr);
      if (pq->thr->flags & (_PR_ON_PAUSEQ | _PR_ON_SLEEPQ)) {
        _PRCPU* cpu = pq->thr->cpu;
        _PR_SLEEPQ_LOCK(pq->thr->cpu);
        _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
        _PR_SLEEPQ_UNLOCK(pq->thr->cpu);

        if (pq->thr->flags & _PR_SUSPENDING) {
          /*
           * set thread state to SUSPENDED;
           * a Resume operation on the thread
           * will move it to the runQ
           */

          pq->thr->state = _PR_SUSPENDED;
          _PR_MISCQ_LOCK(pq->thr->cpu);
          _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
          _PR_MISCQ_UNLOCK(pq->thr->cpu);
        } else {
          pri = pq->thr->priority;
          pq->thr->state = _PR_RUNNABLE;

          _PR_RUNQ_LOCK(cpu);
          _PR_ADD_RUNQ(pq->thr, cpu, pri);
          _PR_RUNQ_UNLOCK(cpu);
        }
      }
      _PR_THREAD_UNLOCK(pq->thr);
    } else {
      if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
        _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
      }
      if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
        _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
      }
    }
  }
  if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
    if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
      _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
    }
  }
}
#  endif /* !defined(_PR_USE_POLL) */

/************************************************************************/

/*
** Called by the scheduler when there is nothing to do. This means that
** all threads are blocked on some monitor somewhere.
**
** Note: this code doesn't release the scheduler lock.
*/

/*
** Pause the current CPU. longjmp to the cpu's pause stack
**
** This must be called with the scheduler locked
*/

void _MD_PauseCPU(PRIntervalTime ticks) {
  PRThread* me = _MD_CURRENT_THREAD();
#  ifdef _PR_USE_POLL
  int timeout;
  struct pollfd* pollfds;   /* an array of pollfd structures */
  struct pollfd* pollfdPtr; /* a pointer that steps through the array */
  unsigned long npollfds;   /* number of pollfd structures in array */
  unsigned long pollfds_size;
  int nfd; /* to hold the return value of poll() */
#  else
  struct timeval timeout, *tvp;
  fd_set r, w, e;
  fd_set *rp, *wp, *ep;
  PRInt32 max_osfd, nfd;
#  endif /* _PR_USE_POLL */
  PRInt32 rv;
  PRCList* q;
  PRUint32 min_timeout;
  sigset_t oldset;

  PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);

  _PR_MD_IOQ_LOCK();

#  ifdef _PR_USE_POLL
  /* Build up the pollfd structure array to wait on */

  /* Find out how many pollfd structures are needed */
  npollfds = _PR_IOQ_OSFD_CNT(me->cpu);
  PR_ASSERT(npollfds >= 0);

  /*
   * We use a pipe to wake up a native thread.  An fd is needed
   * for the pipe and we poll it for reading.
   */

  if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
    npollfds++;
  }

  /*
   * if the cpu's pollfd array is not big enough, release it and allocate a new
   * one
   */

  if (npollfds > _PR_IOQ_POLLFDS_SIZE(me->cpu)) {
    if (_PR_IOQ_POLLFDS(me->cpu) != NULL) {
      PR_DELETE(_PR_IOQ_POLLFDS(me->cpu));
    }
    pollfds_size = PR_MAX(_PR_IOQ_MIN_POLLFDS_SIZE(me->cpu), npollfds);
    pollfds = (struct pollfd*)PR_MALLOC(pollfds_size * sizeof(struct pollfd));
    _PR_IOQ_POLLFDS(me->cpu) = pollfds;
    _PR_IOQ_POLLFDS_SIZE(me->cpu) = pollfds_size;
  } else {
    pollfds = _PR_IOQ_POLLFDS(me->cpu);
  }
  pollfdPtr = pollfds;

  /*
   * If we need to poll the pipe for waking up a native thread,
   * the pipe's fd is the first element in the pollfds array.
   */

  if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
    pollfdPtr->fd = _pr_md_pipefd[0];
    pollfdPtr->events = POLLIN;
    pollfdPtr++;
  }

  min_timeout = PR_INTERVAL_NO_TIMEOUT;
  for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
    PRPollQueue* pq = _PR_POLLQUEUE_PTR(q);
    _PRUnixPollDesc* pds = pq->pds;
    _PRUnixPollDesc* epds = pds + pq->npds;

    if (pq->timeout < min_timeout) {
      min_timeout = pq->timeout;
    }
    for (; pds < epds; pds++, pollfdPtr++) {
      /*
       * Assert that the pollfdPtr pointer does not go
       * beyond the end of the pollfds array
       */

      PR_ASSERT(pollfdPtr < pollfds + npollfds);
      pollfdPtr->fd = pds->osfd;
      /* direct copy of poll flags */
      pollfdPtr->events = pds->in_flags;
    }
  }
  _PR_IOQ_TIMEOUT(me->cpu) = min_timeout;
#  else
  /*
   * assigment of fd_sets
   */

  r = _PR_FD_READ_SET(me->cpu);
  w = _PR_FD_WRITE_SET(me->cpu);
  e = _PR_FD_EXCEPTION_SET(me->cpu);

  rp = &r;
  wp = &w;
  ep = &e;

  max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
  min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
#  endif /* _PR_USE_POLL */
  /*
  ** Compute the minimum timeout value: make it the smaller of the
  ** timeouts specified by the i/o pollers or the timeout of the first
  ** sleeping thread.
  */

  q = _PR_SLEEPQ(me->cpu).next;

  if (q != &_PR_SLEEPQ(me->cpu)) {
    PRThread* t = _PR_THREAD_PTR(q);

    if (t->sleep < min_timeout) {
      min_timeout = t->sleep;
    }
  }
  if (min_timeout > ticks) {
    min_timeout = ticks;
  }

#  ifdef _PR_USE_POLL
  if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
    timeout = -1;
  } else {
    timeout = PR_IntervalToMilliseconds(min_timeout);
  }
#  else
  if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
    tvp = NULL;
  } else {
    timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
    timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout) % PR_USEC_PER_SEC;
    tvp = &timeout;
  }
#  endif /* _PR_USE_POLL */

  _PR_MD_IOQ_UNLOCK();
  _MD_CHECK_FOR_EXIT();
  /*
   * check for i/o operations
   */

#  ifndef _PR_NO_CLOCK_TIMER
  /*
   * Disable the clock interrupts while we are in select, if clock interrupts
   * are enabled. Otherwise, when the select/poll calls are interrupted, the
   * timer value starts ticking from zero again when the system call is
   * restarted.
   */

  if (!_nspr_noclock) {
    PR_ASSERT(sigismember(&timer_set, SIGALRM));
  }
  sigprocmask(SIG_BLOCK, &timer_set, &oldset);
#  endif /* !_PR_NO_CLOCK_TIMER */

#  ifndef _PR_USE_POLL
  PR_ASSERT(FD_ISSET(_pr_md_pipefd[0], rp));
  nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
#  else
  nfd = _MD_POLL(pollfds, npollfds, timeout);
#  endif /* !_PR_USE_POLL */

#  ifndef _PR_NO_CLOCK_TIMER
  if (!_nspr_noclock) {
    sigprocmask(SIG_SETMASK, &oldset, 0);
  }
#  endif /* !_PR_NO_CLOCK_TIMER */

  _MD_CHECK_FOR_EXIT();

  _PR_MD_primordial_cpu();

  _PR_MD_IOQ_LOCK();
  /*
  ** Notify monitors that are associated with the selected descriptors.
  */

#  ifdef _PR_USE_POLL
  if (nfd > 0) {
    pollfdPtr = pollfds;
    if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
      /*
       * Assert that the pipe is the first element in the
       * pollfds array.
       */

      PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]);
      if ((pollfds[0].revents & POLLIN) && (nfd == 1)) {
        /*
         * woken up by another thread; read all the data
         * in the pipe to empty the pipe
         */

        while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) ==
               PIPE_BUF) {
        }
        PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN)));
      }
      pollfdPtr++;
    }
    for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) {
      PRPollQueue* pq = _PR_POLLQUEUE_PTR(q);
      PRBool notify = PR_FALSE;
      _PRUnixPollDesc* pds = pq->pds;
      _PRUnixPollDesc* epds = pds + pq->npds;

      for (; pds < epds; pds++, pollfdPtr++) {
        /*
         * Assert that the pollfdPtr pointer does not go beyond
         * the end of the pollfds array.
         */

        PR_ASSERT(pollfdPtr < pollfds + npollfds);
        /*
         * Assert that the fd's in the pollfds array (stepped
         * through by pollfdPtr) are in the same order as
         * the fd's in _PR_IOQ() (stepped through by q and pds).
         * This is how the pollfds array was created earlier.
         */

        PR_ASSERT(pollfdPtr->fd == pds->osfd);
        pds->out_flags = pollfdPtr->revents;
        /* Negative fd's are ignored by poll() */
        if (pds->osfd >= 0 && pds->out_flags) {
          notify = PR_TRUE;
        }
      }
      if (notify) {
        PRIntn pri;
        PRThread* thred;

        PR_REMOVE_LINK(&pq->links);
        pq->on_ioq = PR_FALSE;

        thred = pq->thr;
        _PR_THREAD_LOCK(thred);
        if (pq->thr->flags & (_PR_ON_PAUSEQ | _PR_ON_SLEEPQ)) {
          _PRCPU* cpu = pq->thr->cpu;
          _PR_SLEEPQ_LOCK(pq->thr->cpu);
          _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
          _PR_SLEEPQ_UNLOCK(pq->thr->cpu);

          if (pq->thr->flags & _PR_SUSPENDING) {
            /*
             * set thread state to SUSPENDED;
             * a Resume operation on the thread
             * will move it to the runQ
             */

            pq->thr->state = _PR_SUSPENDED;
            _PR_MISCQ_LOCK(pq->thr->cpu);
            _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
            _PR_MISCQ_UNLOCK(pq->thr->cpu);
          } else {
            pri = pq->thr->priority;
            pq->thr->state = _PR_RUNNABLE;

            _PR_RUNQ_LOCK(cpu);
            _PR_ADD_RUNQ(pq->thr, cpu, pri);
            _PR_RUNQ_UNLOCK(cpu);
            if (_pr_md_idle_cpus > 1) {
              _PR_MD_WAKEUP_WAITER(thred);
            }
          }
        }
        _PR_THREAD_UNLOCK(thred);
        _PR_IOQ_OSFD_CNT(me->cpu) -= pq->npds;
        PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
      }
    }
  } else if (nfd == -1) {
    PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno));
  }

#  else
  if (nfd > 0) {
    q = _PR_IOQ(me->cpu).next;
    _PR_IOQ_MAX_OSFD(me->cpu) = -1;
    _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
    while (q != &_PR_IOQ(me->cpu)) {
      PRPollQueue* pq = _PR_POLLQUEUE_PTR(q);
      PRBool notify = PR_FALSE;
      _PRUnixPollDesc* pds = pq->pds;
      _PRUnixPollDesc* epds = pds + pq->npds;
      PRInt32 pq_max_osfd = -1;

      q = q->next;
      for (; pds < epds; pds++) {
        PRInt32 osfd = pds->osfd;
        PRInt16 in_flags = pds->in_flags;
        PRInt16 out_flags = 0;
        PR_ASSERT(osfd >= 0 || in_flags == 0);
        if ((in_flags & _PR_UNIX_POLL_READ) && FD_ISSET(osfd, rp)) {
          out_flags |= _PR_UNIX_POLL_READ;
        }
        if ((in_flags & _PR_UNIX_POLL_WRITE) && FD_ISSET(osfd, wp)) {
          out_flags |= _PR_UNIX_POLL_WRITE;
        }
        if ((in_flags & _PR_UNIX_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
          out_flags |= _PR_UNIX_POLL_EXCEPT;
        }
        pds->out_flags = out_flags;
        if (out_flags) {
          notify = PR_TRUE;
        }
        if (osfd > pq_max_osfd) {
          pq_max_osfd = osfd;
        }
      }
      if (notify == PR_TRUE) {
        PRIntn pri;
        PRThread* thred;

        PR_REMOVE_LINK(&pq->links);
        pq->on_ioq = PR_FALSE;

        /*
         * Decrement the count of descriptors for each desciptor/event
         * because this I/O request is being removed from the
         * ioq
         */

        pds = pq->pds;
        for (; pds < epds; pds++) {
          PRInt32 osfd = pds->osfd;
          PRInt16 in_flags = pds->in_flags;
          PR_ASSERT(osfd >= 0 || in_flags == 0);
          if (in_flags & _PR_UNIX_POLL_READ) {
            if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
              FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
            }
          }
          if (in_flags & _PR_UNIX_POLL_WRITE) {
            if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
              FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
            }
          }
          if (in_flags & _PR_UNIX_POLL_EXCEPT) {
            if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
              FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
            }
          }
        }

        /*
         * Because this thread can run on a different cpu right
         * after being added to the run queue, do not dereference
         * pq
         */

        thred = pq->thr;
        _PR_THREAD_LOCK(thred);
        if (pq->thr->flags & (_PR_ON_PAUSEQ | _PR_ON_SLEEPQ)) {
          _PRCPU* cpu = thred->cpu;
          _PR_SLEEPQ_LOCK(pq->thr->cpu);
          _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
          _PR_SLEEPQ_UNLOCK(pq->thr->cpu);

          if (pq->thr->flags & _PR_SUSPENDING) {
            /*
             * set thread state to SUSPENDED;
             * a Resume operation on the thread
             * will move it to the runQ
             */

            pq->thr->state = _PR_SUSPENDED;
            _PR_MISCQ_LOCK(pq->thr->cpu);
            _PR_ADD_SUSPENDQ(pq->thr, pq->thr->cpu);
            _PR_MISCQ_UNLOCK(pq->thr->cpu);
          } else {
            pri = pq->thr->priority;
            pq->thr->state = _PR_RUNNABLE;

            pq->thr->cpu = cpu;
            _PR_RUNQ_LOCK(cpu);
            _PR_ADD_RUNQ(pq->thr, cpu, pri);
            _PR_RUNQ_UNLOCK(cpu);
            if (_pr_md_idle_cpus > 1) {
              _PR_MD_WAKEUP_WAITER(thred);
            }
          }
        }
        _PR_THREAD_UNLOCK(thred);
      } else {
        if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
          _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
        }
        if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
          _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
        }
      }
    }
    if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
      if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) {
        /*
         * woken up by another thread; read all the data
         * in the pipe to empty the pipe
         */

        while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) ==
               PIPE_BUF) {
        }
        PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN)));
      }
      if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
        _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
      }
    }
  } else if (nfd < 0) {
    if (errno == EBADF) {
      FindBadFDs();
    } else {
      PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d", errno));
    }
  } else {
    PR_ASSERT(nfd == 0);
    /*
     * compute the new value of _PR_IOQ_TIMEOUT
     */

    q = _PR_IOQ(me->cpu).next;
    _PR_IOQ_MAX_OSFD(me->cpu) = -1;
    _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
    while (q != &_PR_IOQ(me->cpu)) {
      PRPollQueue* pq = _PR_POLLQUEUE_PTR(q);
      _PRUnixPollDesc* pds = pq->pds;
      _PRUnixPollDesc* epds = pds + pq->npds;
      PRInt32 pq_max_osfd = -1;

      q = q->next;
      for (; pds < epds; pds++) {
        if (pds->osfd > pq_max_osfd) {
          pq_max_osfd = pds->osfd;
        }
      }
      if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu)) {
        _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
      }
      if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd) {
        _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
      }
    }
    if (_PR_IS_NATIVE_THREAD_SUPPORTED()) {
      if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) {
        _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
      }
    }
  }
#  endif /* _PR_USE_POLL */
  _PR_MD_IOQ_UNLOCK();
}

void _MD_Wakeup_CPUs() {
  PRInt32 rv, data;

  data = 0;
  rv = write(_pr_md_pipefd[1], &data, 1);

  while ((rv < 0) && (errno == EAGAIN)) {
    /*
     * pipe full, read all data in pipe to empty it
     */

    while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) ==
           PIPE_BUF) {
    }
    PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN)));
    rv = write(_pr_md_pipefd[1], &data, 1);
  }
}

void _MD_InitCPUS() {
  PRInt32 rv, flags;
  PRThread* me = _MD_CURRENT_THREAD();

  rv = pipe(_pr_md_pipefd);
  PR_ASSERT(rv == 0);
  _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0];
#  ifndef _PR_USE_POLL
  FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu));
#  endif

  flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0);
  fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK);
  flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0);
  fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK);
}

/*
** Unix SIGALRM (clock) signal handler
*/

static void ClockInterruptHandler() {
  int olderrno;
  PRUintn pri;
  _PRCPU* cpu = _PR_MD_CURRENT_CPU();
  PRThread* me = _MD_CURRENT_THREAD();

#  ifdef SOLARIS
  if (!me || _PR_IS_NATIVE_THREAD(me)) {
    _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK;
    return;
  }
#  endif

  if (_PR_MD_GET_INTSOFF() != 0) {
    cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK;
    return;
  }
  _PR_MD_SET_INTSOFF(1);

  olderrno = errno;
  _PR_ClockInterrupt();
  errno = olderrno;

  /*
  ** If the interrupt wants a resched or if some other thread at
  ** the same priority needs the cpu, reschedule.
  */

  pri = me->priority;
  if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) {
#  ifdef _PR_NO_PREEMPT
    cpu->resched = PR_TRUE;
    if (pr_interruptSwitchHook) {
      (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg);
    }
#  else  /* _PR_NO_PREEMPT */
    /*
    ** Re-enable unix interrupts (so that we can use
    ** setjmp/longjmp for context switching without having to
    ** worry about the signal state)
    */

    sigprocmask(SIG_SETMASK, &empty_set, 0);
    PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch"));

    if (!(me->flags & _PR_IDLE_THREAD)) {
      _PR_THREAD_LOCK(me);
      me->state = _PR_RUNNABLE;
      me->cpu = cpu;
      _PR_RUNQ_LOCK(cpu);
      _PR_ADD_RUNQ(me, cpu, pri);
      _PR_RUNQ_UNLOCK(cpu);
      _PR_THREAD_UNLOCK(me);
    } else {
      me->state = _PR_RUNNABLE;
    }
    _MD_SWITCH_CONTEXT(me);
    PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch"));
#  endif /* _PR_NO_PREEMPT */
  }
  /*
   * Because this thread could be running on a different cpu after
   * a context switch the current cpu should be accessed and the
   * value of the 'cpu' variable should not be used.
   */

  _PR_MD_SET_INTSOFF(0);
}

/* # of milliseconds per clock tick that we will use */
#  define MSEC_PER_TICK 50

void _MD_StartInterrupts() {
  char* eval;

  if ((eval = getenv("NSPR_NOCLOCK")) != NULL) {
    if (atoi(eval) == 0) {
      _nspr_noclock = 0;
    } else {
      _nspr_noclock = 1;
    }
  }

#  ifndef _PR_NO_CLOCK_TIMER
  if (!_nspr_noclock) {
    _MD_EnableClockInterrupts();
  }
#  endif
}

void _MD_StopInterrupts() { sigprocmask(SIG_BLOCK, &timer_set, 0); }

void _MD_EnableClockInterrupts() {
  struct itimerval itval;
  extern PRUintn _pr_numCPU;
  struct sigaction vtact;

  vtact.sa_handler = (void (*)())ClockInterruptHandler;
  sigemptyset(&vtact.sa_mask);
  vtact.sa_flags = SA_RESTART;
  sigaction(SIGALRM, &vtact, 0);

  PR_ASSERT(_pr_numCPU == 1);
  itval.it_interval.tv_sec = 0;
  itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC;
  itval.it_value = itval.it_interval;
  setitimer(ITIMER_REAL, &itval, 0);
}

void _MD_DisableClockInterrupts() {
  struct itimerval itval;
  extern PRUintn _pr_numCPU;

  PR_ASSERT(_pr_numCPU == 1);
  itval.it_interval.tv_sec = 0;
  itval.it_interval.tv_usec = 0;
  itval.it_value = itval.it_interval;
  setitimer(ITIMER_REAL, &itval, 0);
}

void _MD_BlockClockInterrupts() { sigprocmask(SIG_BLOCK, &timer_set, 0); }

void _MD_UnblockClockInterrupts() { sigprocmask(SIG_UNBLOCK, &timer_set, 0); }

void _MD_MakeNonblock(PRFileDesc* fd) {
  PRInt32 osfd = fd->secret->md.osfd;
  int flags;

  if (osfd <= 2) {
    /* Don't mess around with stdin, stdout or stderr */
    return;
  }
  flags = fcntl(osfd, F_GETFL, 0);

  /*
   * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible.
   * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O),
   * otherwise connect() still blocks and can be interrupted by SIGALRM.
   */


  fcntl(osfd, F_SETFL, flags | O_NONBLOCK);
}

PRInt32 _MD_open(const char* name, PRIntn flags, PRIntn mode) {
  PRInt32 osflags;
  PRInt32 rv, err;

  if (flags & PR_RDWR) {
    osflags = O_RDWR;
  } else if (flags & PR_WRONLY) {
    osflags = O_WRONLY;
  } else {
    osflags = O_RDONLY;
  }

  if (flags & PR_EXCL) {
    osflags |= O_EXCL;
  }
  if (flags & PR_APPEND) {
    osflags |= O_APPEND;
  }
  if (flags & PR_TRUNCATE) {
    osflags |= O_TRUNC;
  }
  if (flags & PR_SYNC) {
#  if defined(O_SYNC)
    osflags |= O_SYNC;
#  elif defined(O_FSYNC)
    osflags |= O_FSYNC;
#  else
#    error "Neither O_SYNC nor O_FSYNC is defined on this platform"
#  endif
  }

  /*
  ** On creations we hold the 'create' lock in order to enforce
  ** the semantics of PR_Rename. (see the latter for more details)
  */

  if (flags & PR_CREATE_FILE) {
    osflags |= O_CREAT;
    if (NULL != _pr_unix_rename_lock) {
      PR_Lock(_pr_unix_rename_lock);
    }
  }

#  if defined(ANDROID)
  osflags |= O_LARGEFILE;
#  endif

  rv = _md_iovector._open64(name, osflags, mode);

  if (rv < 0) {
    err = _MD_ERRNO();
    _PR_MD_MAP_OPEN_ERROR(err);
  }

  if ((flags & PR_CREATE_FILE) && (NULL != _pr_unix_rename_lock)) {
    PR_Unlock(_pr_unix_rename_lock);
  }
  return rv;
}

PRIntervalTime intr_timeout_ticks;

#  if defined(SOLARIS)
static void sigsegvhandler() {
  fprintf(stderr, "Received SIGSEGV\n");
  fflush(stderr);
  pause();
}

static void sigaborthandler() {
  fprintf(stderr, "Received SIGABRT\n");
  fflush(stderr);
  pause();
}

static void sigbushandler() {
  fprintf(stderr, "Received SIGBUS\n");
  fflush(stderr);
  pause();
}
#  endif /* SOLARIS */

#endif /* !defined(_PR_PTHREADS) */

void _MD_query_fd_inheritable(PRFileDesc* fd) {
  int flags;

  PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
  flags = fcntl(fd->secret->md.osfd, F_GETFD, 0);
  PR_ASSERT(-1 != flags);
  fd->secret->inheritable = (flags & FD_CLOEXEC) ? _PR_TRI_FALSE : _PR_TRI_TRUE;
}

PROffset32 _MD_lseek(PRFileDesc* fd, PROffset32 offset, PRSeekWhence whence) {
  PROffset32 rv, where;

  switch (whence) {
    case PR_SEEK_SET:
      where = SEEK_SET;
      break;
    case PR_SEEK_CUR:
      where = SEEK_CUR;
      break;
    case PR_SEEK_END:
      where = SEEK_END;
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      rv = -1;
      goto done;
  }
  rv = lseek(fd->secret->md.osfd, offset, where);
  if (rv == -1) {
    PRInt32 syserr = _MD_ERRNO();
    _PR_MD_MAP_LSEEK_ERROR(syserr);
  }
done:
  return (rv);
}

PROffset64 _MD_lseek64(PRFileDesc* fd, PROffset64 offset, PRSeekWhence whence) {
  PRInt32 where;
  PROffset64 rv;

  switch (whence) {
    case PR_SEEK_SET:
      where = SEEK_SET;
      break;
    case PR_SEEK_CUR:
      where = SEEK_CUR;
      break;
    case PR_SEEK_END:
      where = SEEK_END;
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      rv = minus_one;
      goto done;
  }
  rv = _md_iovector._lseek64(fd->secret->md.osfd, offset, where);
  if (LL_EQ(rv, minus_one)) {
    PRInt32 syserr = _MD_ERRNO();
    _PR_MD_MAP_LSEEK_ERROR(syserr);
  }
done:
  return rv;
/* _MD_lseek64 */

/*
** _MD_set_fileinfo_times --
**     Set the modifyTime and creationTime of the PRFileInfo
**     structure using the values in struct stat.
**
** _MD_set_fileinfo64_times --
**     Set the modifyTime and creationTime of the PRFileInfo64
**     structure using the values in _MDStat64.
*/


#if defined(_PR_STAT_HAS_ST_ATIM)
/*
** struct stat has st_atim, st_mtim, and st_ctim fields of
** type timestruc_t.
*/

static void _MD_set_fileinfo_times(const struct stat* sb, PRFileInfo* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtim.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtim.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctim.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctim.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}

static void _MD_set_fileinfo64_times(const _MDStat64* sb, PRFileInfo64* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtim.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtim.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctim.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctim.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}
#elif defined(_PR_STAT_HAS_ST_ATIM_UNION)
/*
** The st_atim, st_mtim, and st_ctim fields in struct stat are
** unions with a st__tim union member of type timestruc_t.
*/

static void _MD_set_fileinfo_times(const struct stat* sb, PRFileInfo* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}

static void _MD_set_fileinfo64_times(const _MDStat64* sb, PRFileInfo64* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtim.st__tim.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtim.st__tim.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctim.st__tim.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctim.st__tim.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}
#elif defined(_PR_STAT_HAS_ST_ATIMESPEC)
/*
** struct stat has st_atimespec, st_mtimespec, and st_ctimespec
** fields of type struct timespec.
*/

#  if defined(_PR_TIMESPEC_HAS_TS_SEC)
static void _MD_set_fileinfo_times(const struct stat* sb, PRFileInfo* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}

static void _MD_set_fileinfo64_times(const _MDStat64* sb, PRFileInfo64* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtimespec.ts_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtimespec.ts_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctimespec.ts_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctimespec.ts_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}
#  else  /* _PR_TIMESPEC_HAS_TS_SEC */
/*
** The POSIX timespec structure has tv_sec and tv_nsec.
*/

static void _MD_set_fileinfo_times(const struct stat* sb, PRFileInfo* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}

static void _MD_set_fileinfo64_times(const _MDStat64* sb, PRFileInfo64* info) {
  PRInt64 us, s2us;

  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(info->modifyTime, sb->st_mtimespec.tv_sec);
  LL_MUL(info->modifyTime, info->modifyTime, s2us);
  LL_I2L(us, sb->st_mtimespec.tv_nsec / 1000);
  LL_ADD(info->modifyTime, info->modifyTime, us);
  LL_I2L(info->creationTime, sb->st_ctimespec.tv_sec);
  LL_MUL(info->creationTime, info->creationTime, s2us);
  LL_I2L(us, sb->st_ctimespec.tv_nsec / 1000);
  LL_ADD(info->creationTime, info->creationTime, us);
}
#  endif /* _PR_TIMESPEC_HAS_TS_SEC */
#elif defined(_PR_STAT_HAS_ONLY_ST_ATIME)
/*
** struct stat only has st_atime, st_mtime, and st_ctime fields
** of type time_t.
*/

static void _MD_set_fileinfo_times(const struct stat* sb, PRFileInfo* info) {
  PRInt64 s, s2us;
  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(s, sb->st_mtime);
  LL_MUL(s, s, s2us);
  info->modifyTime = s;
  LL_I2L(s, sb->st_ctime);
  LL_MUL(s, s, s2us);
  info->creationTime = s;
}

static void _MD_set_fileinfo64_times(const _MDStat64* sb, PRFileInfo64* info) {
  PRInt64 s, s2us;
  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(s, sb->st_mtime);
  LL_MUL(s, s, s2us);
  info->modifyTime = s;
  LL_I2L(s, sb->st_ctime);
  LL_MUL(s, s, s2us);
  info->creationTime = s;
}
#else
#  error "I don't know yet"
#endif

static int _MD_convert_stat_to_fileinfo(const struct stat* sb,
                                        PRFileInfo* info) {
  if (S_IFREG & sb->st_mode) {
    info->type = PR_FILE_FILE;
  } else if (S_IFDIR & sb->st_mode) {
    info->type = PR_FILE_DIRECTORY;
  } else {
    info->type = PR_FILE_OTHER;
  }

#if defined(_PR_HAVE_LARGE_OFF_T)
  if (0x7fffffffL < sb->st_size) {
    PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
    return -1;
  }
#endif /* defined(_PR_HAVE_LARGE_OFF_T) */
  info->size = sb->st_size;

  _MD_set_fileinfo_times(sb, info);
  return 0;
/* _MD_convert_stat_to_fileinfo */

static int _MD_convert_stat64_to_fileinfo64(const _MDStat64* sb,
                                            PRFileInfo64* info) {
  if (S_IFREG & sb->st_mode) {
    info->type = PR_FILE_FILE;
  } else if (S_IFDIR & sb->st_mode) {
    info->type = PR_FILE_DIRECTORY;
  } else {
    info->type = PR_FILE_OTHER;
  }

  LL_I2L(info->size, sb->st_size);

  _MD_set_fileinfo64_times(sb, info);
  return 0;
/* _MD_convert_stat64_to_fileinfo64 */

PRInt32 _MD_getfileinfo(const char* fn, PRFileInfo* info) {
  PRInt32 rv;
  struct stat sb;

  rv = stat(fn, &sb);
  if (rv < 0) {
    _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
  } else if (NULL != info) {
    rv = _MD_convert_stat_to_fileinfo(&sb, info);
  }
  return rv;
}

PRInt32 _MD_getfileinfo64(const char* fn, PRFileInfo64* info) {
  _MDStat64 sb;
  PRInt32 rv = _md_iovector._stat64(fn, &sb);
  if (rv < 0) {
    _PR_MD_MAP_STAT_ERROR(_MD_ERRNO());
  } else if (NULL != info) {
    rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
  }
  return rv;
}

PRInt32 _MD_getopenfileinfo(const PRFileDesc* fd, PRFileInfo* info) {
  struct stat sb;
  PRInt32 rv = fstat(fd->secret->md.osfd, &sb);
  if (rv < 0) {
    _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
  } else if (NULL != info) {
    rv = _MD_convert_stat_to_fileinfo(&sb, info);
  }
  return rv;
}

PRInt32 _MD_getopenfileinfo64(const PRFileDesc* fd, PRFileInfo64* info) {
  _MDStat64 sb;
  PRInt32 rv = _md_iovector._fstat64(fd->secret->md.osfd, &sb);
  if (rv < 0) {
    _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO());
  } else if (NULL != info) {
    rv = _MD_convert_stat64_to_fileinfo64(&sb, info);
  }
  return rv;
}

/*
 * _md_iovector._open64 must be initialized to 'open' so that _PR_InitLog can
 * open the log file during NSPR initialization, before _md_iovector is
 * initialized by _PR_MD_FINAL_INIT.  This means the log file cannot be a
 * large file on some platforms.
 */

struct _MD_IOVector _md_iovector = {open};

/*
** These implementations are to emulate large file routines on systems that
** don't have them. Their goal is to check in case overflow occurs. Otherwise
** they will just operate as normal using 32-bit file routines.
**
** The checking might be pre- or post-op, depending on the semantics.
*/


#if defined(SOLARIS2_5)

static PRIntn _MD_solaris25_fstat64(PRIntn osfd, _MDStat64* buf) {
  PRInt32 rv;
  struct stat sb;

  rv = fstat(osfd, &sb);
  if (rv >= 0) {
    /*
    ** I'm only copying the fields that are immediately needed.
    ** If somebody else calls this function, some of the fields
    ** may not be defined.
    */

    (void)memset(buf, 0, sizeof(_MDStat64));
    buf->st_mode = sb.st_mode;
    buf->st_ctim = sb.st_ctim;
    buf->st_mtim = sb.st_mtim;
    buf->st_size = sb.st_size;
  }
  return rv;
/* _MD_solaris25_fstat64 */

static PRIntn _MD_solaris25_stat64(const char* fn, _MDStat64* buf) {
  PRInt32 rv;
  struct stat sb;

  rv = stat(fn, &sb);
  if (rv >= 0) {
    /*
    ** I'm only copying the fields that are immediately needed.
    ** If somebody else calls this function, some of the fields
    ** may not be defined.
    */

    (void)memset(buf, 0, sizeof(_MDStat64));
    buf->st_mode = sb.st_mode;
    buf->st_ctim = sb.st_ctim;
    buf->st_mtim = sb.st_mtim;
    buf->st_size = sb.st_size;
  }
  return rv;
/* _MD_solaris25_stat64 */
#endif /* defined(SOLARIS2_5) */

#if defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5)

static PROffset64 _MD_Unix_lseek64(PRIntn osfd, PROffset64 offset,
                                   PRIntn whence) {
  PRUint64 maxoff;
  PROffset64 rv = minus_one;
  LL_I2L(maxoff, 0x7fffffff);
  if (LL_CMP(offset, <=, maxoff)) {
    off_t off;
    LL_L2I(off, offset);
    LL_I2L(rv, lseek(osfd, off, whence));
  } else {
    errno = EFBIG; /* we can't go there */
  }
  return rv;
/* _MD_Unix_lseek64 */

static void* _MD_Unix_mmap64(void* addr, PRSize len, PRIntn prot, PRIntn flags,
                             PRIntn fildes, PRInt64 offset) {
  PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
  return NULL;
/* _MD_Unix_mmap64 */
#endif /* defined(_PR_NO_LARGE_FILES) || defined(SOLARIS2_5) */

/* NDK non-unified headers for API < 21 don't have mmap64. However,
 * NDK unified headers do provide mmap64 for all API versions when building
 * with clang. Therefore, we should provide mmap64 here for API < 21 if we're
 * not using clang or if we're using non-unified headers. We check for
 * non-unified headers by the lack of __ANDROID_API_L__ macro. */

#if defined(ANDROID) && __ANDROID_API__ < 21 && \
    (!defined(__clang__) || !defined(__ANDROID_API_L__))
PR_IMPORT(void) * __mmap2(void*, size_t, intintint, size_t);

#  define ANDROID_PAGE_SIZE 4096

static void* mmap64(void* addr, size_t len, int prot, int flags, int fd,
                    loff_t offset) {
  if (offset & (ANDROID_PAGE_SIZE - 1)) {
    errno = EINVAL;
    return MAP_FAILED;
  }
  return __mmap2(addr, len, prot, flags, fd, offset / ANDROID_PAGE_SIZE);
}
#endif

static void _PR_InitIOV(void) {
#if defined(SOLARIS2_5)
  PRLibrary* lib;
  void* open64_func;

  open64_func = PR_FindSymbolAndLibrary("open64", &lib);
  if (NULL != open64_func) {
    PR_ASSERT(NULL != lib);
    _md_iovector._open64 = (_MD_Open64)open64_func;
    _md_iovector._mmap64 = (_MD_Mmap64)PR_FindSymbol(lib, "mmap64");
    _md_iovector._fstat64 = (_MD_Fstat64)PR_FindSymbol(lib, "fstat64");
    _md_iovector._stat64 = (_MD_Stat64)PR_FindSymbol(lib, "stat64");
    _md_iovector._lseek64 = (_MD_Lseek64)PR_FindSymbol(lib, "lseek64");
    (void)PR_UnloadLibrary(lib);
  } else {
    _md_iovector._open64 = open;
    _md_iovector._mmap64 = _MD_Unix_mmap64;
    _md_iovector._fstat64 = _MD_solaris25_fstat64;
    _md_iovector._stat64 = _MD_solaris25_stat64;
    _md_iovector._lseek64 = _MD_Unix_lseek64;
  }
#elif defined(_PR_NO_LARGE_FILES)
  _md_iovector._open64 = open;
  _md_iovector._mmap64 = _MD_Unix_mmap64;
  _md_iovector._fstat64 = fstat;
  _md_iovector._stat64 = stat;
  _md_iovector._lseek64 = _MD_Unix_lseek64;
#elif defined(_PR_HAVE_OFF64_T)
#  if (defined(ANDROID) && __ANDROID_API__ < 21)
  /*
   * Android < 21 doesn't have open64.  We pass the O_LARGEFILE flag to open
   * in _MD_open.
   */

  _md_iovector._open64 = open;
#  else
  _md_iovector._open64 = open64;
#  endif
  _md_iovector._mmap64 = mmap64;
#  if (defined(ANDROID) && __ANDROID_API__ < 21)
  /* Same as the open64 case for Android. */
  _md_iovector._fstat64 = (_MD_Fstat64)fstat;
  _md_iovector._stat64 = (_MD_Stat64)stat;
#  else
  _md_iovector._fstat64 = fstat64;
  _md_iovector._stat64 = stat64;
#  endif
  _md_iovector._lseek64 = lseek64;
#elif defined(_PR_HAVE_LARGE_OFF_T)
  _md_iovector._open64 = open;
  _md_iovector._mmap64 = mmap;
  _md_iovector._fstat64 = fstat;
  _md_iovector._stat64 = stat;
  _md_iovector._lseek64 = lseek;
#else
#  error "I don't know yet"
#endif
  LL_I2L(minus_one, -1);
/* _PR_InitIOV */

void _PR_UnixInit(void) {
  struct sigaction sigact;
  int rv;

  sigemptyset(&timer_set);

#if !defined(_PR_PTHREADS)

  sigaddset(&timer_set, SIGALRM);
  sigemptyset(&empty_set);
  intr_timeout_ticks = PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS);

#  if defined(SOLARIS)

  if (getenv("NSPR_SIGSEGV_HANDLE")) {
    sigact.sa_handler = sigsegvhandler;
    sigact.sa_flags = 0;
    sigact.sa_mask = timer_set;
    sigaction(SIGSEGV, &sigact, 0);
  }

  if (getenv("NSPR_SIGABRT_HANDLE")) {
    sigact.sa_handler = sigaborthandler;
    sigact.sa_flags = 0;
    sigact.sa_mask = timer_set;
    sigaction(SIGABRT, &sigact, 0);
  }

  if (getenv("NSPR_SIGBUS_HANDLE")) {
    sigact.sa_handler = sigbushandler;
    sigact.sa_flags = 0;
    sigact.sa_mask = timer_set;
    sigaction(SIGBUS, &sigact, 0);
  }

#  endif
#endif /* !defined(_PR_PTHREADS) */

  sigact.sa_handler = SIG_IGN;
  sigemptyset(&sigact.sa_mask);
  sigact.sa_flags = 0;
  rv = sigaction(SIGPIPE, &sigact, 0);
  PR_ASSERT(0 == rv);

  _pr_unix_rename_lock = PR_NewLock();
  PR_ASSERT(NULL != _pr_unix_rename_lock);
  _pr_Xfe_mon = PR_NewMonitor();
  PR_ASSERT(NULL != _pr_Xfe_mon);

  _PR_InitIOV(); /* one last hack */
}

void _PR_UnixCleanup(void) {
  if (_pr_unix_rename_lock) {
    PR_DestroyLock(_pr_unix_rename_lock);
    _pr_unix_rename_lock = NULL;
  }
  if (_pr_Xfe_mon) {
    PR_DestroyMonitor(_pr_Xfe_mon);
    _pr_Xfe_mon = NULL;
  }
}

#if !defined(_PR_PTHREADS)

/*
 * Variables used by the GC code, initialized in _MD_InitSegs().
 */

static PRInt32 _pr_zero_fd = -1;
static PRLock* _pr_md_lock = NULL;

/*
 * _MD_InitSegs --
 *
 * This is Unix's version of _PR_MD_INIT_SEGS(), which is
 * called by _PR_InitSegs(), which in turn is called by
 * PR_Init().
 */

void _MD_InitSegs(void) {
#  ifdef DEBUG
  /*
  ** Disable using mmap(2) if NSPR_NO_MMAP is set
  */

  if (getenv("NSPR_NO_MMAP")) {
    _pr_zero_fd = -2;
    return;
  }
#  endif
  _pr_zero_fd = open("/dev/zero", O_RDWR, 0);
  /* Prevent the fd from being inherited by child processes */
  fcntl(_pr_zero_fd, F_SETFD, FD_CLOEXEC);
  _pr_md_lock = PR_NewLock();
}

PRStatus _MD_AllocSegment(PRSegment* seg, PRUint32 size, void* vaddr) {
  static char* lastaddr = (char*)_PR_STACK_VMBASE;
  PRStatus retval = PR_SUCCESS;
  int prot;
  void* rv;

  PR_ASSERT(seg != 0);
  PR_ASSERT(size != 0);

  PR_Lock(_pr_md_lock);
  if (_pr_zero_fd < 0) {
  from_heap:
    seg->vaddr = PR_MALLOC(size);
    if (!seg->vaddr) {
      retval = PR_FAILURE;
    } else {
      seg->size = size;
    }
    goto exit;
  }

  prot = PROT_READ | PROT_WRITE;
  /*
   * On Alpha Linux, the user-level thread stack needs
   * to be made executable because longjmp/signal seem
   * to put machine instructions on the stack.
   */

#  if defined(LINUX) && defined(__alpha)
  prot |= PROT_EXEC;
#  endif
  rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot, _MD_MMAP_FLAGS,
            _pr_zero_fd, 0);
  if (rv == (void*)-1) {
    goto from_heap;
  }
  lastaddr += size;
  seg->vaddr = rv;
  seg->size = size;
  seg->flags = _PR_SEG_VM;

exit:
  PR_Unlock(_pr_md_lock);
  return retval;
}

void _MD_FreeSegment(PRSegment* seg) {
  if (seg->flags & _PR_SEG_VM) {
    (void)munmap(seg->vaddr, seg->size);
  } else {
    PR_DELETE(seg->vaddr);
  }
}

#endif /* _PR_PTHREADS */

/*
 *-----------------------------------------------------------------------
 *
 * PR_Now --
 *
 *     Returns the current time in microseconds since the epoch.
 *     The epoch is midnight January 1, 1970 GMT.
 *     The implementation is machine dependent.  This is the Unix
 *     implementation.
 *     Cf. time_t time(time_t *tp)
 *
 *-----------------------------------------------------------------------
 */


PR_IMPLEMENT(PRTime)
PR_Now(void) {
  struct timeval tv;
  PRInt64 s, us, s2us;

  GETTIMEOFDAY(&tv);
  LL_I2L(s2us, PR_USEC_PER_SEC);
  LL_I2L(s, tv.tv_sec);
  LL_I2L(us, tv.tv_usec);
  LL_MUL(s, s, s2us);
  LL_ADD(s, s, us);
  return s;
}

#if defined(_MD_INTERVAL_USE_GTOD)
/*
 * This version of interval times is based on the time of day
 * capability offered by the system. This isn't valid for two reasons:
 * 1) The time of day is neither linear nor montonically increasing
 * 2) The units here are milliseconds. That's not appropriate for our use.
 */

PRIntervalTime _PR_UNIX_GetInterval() {
  struct timeval time;
  PRIntervalTime ticks;

  (void)GETTIMEOFDAY(&time);                       /* fallicy of course */
  ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */
  ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */
  return ticks;
/* _PR_UNIX_GetInterval */

PRIntervalTime _PR_UNIX_TicksPerSecond() {
  return 1000; /* this needs some work :) */
}
#endif

#if defined(_PR_HAVE_CLOCK_MONOTONIC)
PRIntervalTime _PR_UNIX_GetInterval2() {
  struct timespec time;
  PRIntervalTime ticks;

  if (clock_gettime(CLOCK_MONOTONIC, &time) != 0) {
    fprintf(stderr, "clock_gettime failed: %d\n", errno);
    abort();
  }

  ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC;
  ticks += (PRUint32)time.tv_nsec / PR_NSEC_PER_MSEC;
  return ticks;
}

PRIntervalTime _PR_UNIX_TicksPerSecond2() { return 1000; }
#endif

#if !defined(_PR_PTHREADS)
/*
 * Wait for I/O on multiple descriptors.
 *
 * Return 0 if timed out, return -1 if interrupted,
 * else return the number of ready descriptors.
 */

PRInt32 _PR_WaitForMultipleFDs(_PRUnixPollDesc* unixpds, PRInt32 pdcnt,
                               PRIntervalTime timeout) {
  PRPollQueue pq;
  PRIntn is;
  PRInt32 rv;
  _PRCPU* io_cpu;
  _PRUnixPollDesc *unixpd, *eunixpd;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));

  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    return -1;
  }

  pq.pds = unixpds;
  pq.npds = pdcnt;

  _PR_INTSOFF(is);
  _PR_MD_IOQ_LOCK();
  _PR_THREAD_LOCK(me);

  pq.thr = me;
  io_cpu = me->cpu;
  pq.on_ioq = PR_TRUE;
  pq.timeout = timeout;
  _PR_ADD_TO_IOQ(pq, me->cpu);

#  if !defined(_PR_USE_POLL)
  eunixpd = unixpds + pdcnt;
  for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
    PRInt32 osfd = unixpd->osfd;
    if (unixpd->in_flags & _PR_UNIX_POLL_READ) {
      FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
      _PR_FD_READ_CNT(me->cpu)[osfd]++;
    }
    if (unixpd->in_flags & _PR_UNIX_POLL_WRITE) {
      FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
      (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
    }
    if (unixpd->in_flags & _PR_UNIX_POLL_EXCEPT) {
      FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
      (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
    }
    if (osfd > _PR_IOQ_MAX_OSFD(me->cpu)) {
      _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
    }
  }
#  endif /* !defined(_PR_USE_POLL) */

  if (_PR_IOQ_TIMEOUT(me->cpu) > timeout) {
    _PR_IOQ_TIMEOUT(me->cpu) = timeout;
  }

  _PR_IOQ_OSFD_CNT(me->cpu) += pdcnt;

  _PR_SLEEPQ_LOCK(me->cpu);
  _PR_ADD_SLEEPQ(me, timeout);
  me->state = _PR_IO_WAIT;
  me->io_pending = PR_TRUE;
  me->io_suspended = PR_FALSE;
  _PR_SLEEPQ_UNLOCK(me->cpu);
  _PR_THREAD_UNLOCK(me);
  _PR_MD_IOQ_UNLOCK();

  _PR_MD_WAIT(me, timeout);

  me->io_pending = PR_FALSE;
  me->io_suspended = PR_FALSE;

  /*
   * This thread should run on the same cpu on which it was blocked; when
   * the IO request times out the fd sets and fd counts for the
   * cpu are updated below.
   */

  PR_ASSERT(me->cpu == io_cpu);

  /*
  ** If we timed out the pollq might still be on the ioq. Remove it
  ** before continuing.
  */

  if (pq.on_ioq) {
    _PR_MD_IOQ_LOCK();
    /*
     * Need to check pq.on_ioq again
     */

    if (pq.on_ioq) {
      PR_REMOVE_LINK(&pq.links);
#  ifndef _PR_USE_POLL
      eunixpd = unixpds + pdcnt;
      for (unixpd = unixpds; unixpd < eunixpd; unixpd++) {
        PRInt32 osfd = unixpd->osfd;
        PRInt16 in_flags = unixpd->in_flags;

        if (in_flags & _PR_UNIX_POLL_READ) {
          if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
          }
        }
        if (in_flags & _PR_UNIX_POLL_WRITE) {
          if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
          }
        }
        if (in_flags & _PR_UNIX_POLL_EXCEPT) {
          if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0) {
            FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
          }
        }
      }
#  endif /* _PR_USE_POLL */
      PR_ASSERT(pq.npds == pdcnt);
      _PR_IOQ_OSFD_CNT(me->cpu) -= pdcnt;
      PR_ASSERT(_PR_IOQ_OSFD_CNT(me->cpu) >= 0);
    }
    _PR_MD_IOQ_UNLOCK();
  }
  /* XXX Should we use _PR_FAST_INTSON or _PR_INTSON? */
  if (1 == pdcnt) {
    _PR_FAST_INTSON(is);
  } else {
    _PR_INTSON(is);
  }

  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    return -1;
  }

  rv = 0;
  if (pq.on_ioq == PR_FALSE) {
    /* Count the number of ready descriptors */
    while (--pdcnt >= 0) {
      if (unixpds->out_flags != 0) {
        rv++;
      }
      unixpds++;
    }
  }

  return rv;
}

/*
 * Unblock threads waiting for I/O
 *    used when interrupting threads
 *
 * NOTE: The thread lock should held when this function is called.
 * On return, the thread lock is released.
 */

void _PR_Unblock_IO_Wait(PRThread* thr) {
  int pri = thr->priority;
  _PRCPU* cpu = thr->cpu;

  /*
   * GLOBAL threads wakeup periodically to check for interrupt
   */

  if (_PR_IS_NATIVE_THREAD(thr)) {
    _PR_THREAD_UNLOCK(thr);
    return;
  }

  PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
  _PR_SLEEPQ_LOCK(cpu);
  _PR_DEL_SLEEPQ(thr, PR_TRUE);
  _PR_SLEEPQ_UNLOCK(cpu);

  PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
  thr->state = _PR_RUNNABLE;
  _PR_RUNQ_LOCK(cpu);
  _PR_ADD_RUNQ(thr, cpu, pri);
  _PR_RUNQ_UNLOCK(cpu);
  _PR_THREAD_UNLOCK(thr);
  _PR_MD_WAKEUP_WAITER(thr);
}
#endif /* !defined(_PR_PTHREADS) */

/*
 * When a nonblocking connect has completed, determine whether it
 * succeeded or failed, and if it failed, what the error code is.
 *
 * The function returns the error code.  An error code of 0 means
 * that the nonblocking connect succeeded.
 */


int _MD_unix_get_nonblocking_connect_error(int osfd) {
#if defined(NTO)
  /* Neutrino does not support the SO_ERROR socket option */
  PRInt32 rv;
  PRNetAddr addr;
  _PRSockLen_t addrlen = sizeof(addr);

  /* Test to see if we are using the Tiny TCP/IP Stack or the Full one. */
  struct statvfs superblock;
  rv = fstatvfs(osfd, &superblock);
  if (rv == 0) {
    if (strcmp(superblock.f_basetype, "ttcpip") == 0) {
      /* Using the Tiny Stack! */
      rv = getpeername(osfd, (struct sockaddr*)&addr, (_PRSockLen_t*)&addrlen);
      if (rv == -1) {
        int errno_copy = errno; /* make a copy so I don't
                                 * accidentally reset */


        if (errno_copy == ENOTCONN) {
          struct stat StatInfo;
          rv = fstat(osfd, &StatInfo);
          if (rv == 0) {
            time_t current_time = time(NULL);

            /*
             * this is a real hack, can't explain why it
             * works it just does
             */

            if (abs(current_time - StatInfo.st_atime) < 5) {
              return ECONNREFUSED;
            } else {
              return ETIMEDOUT;
            }
          } else {
            return ECONNREFUSED;
          }
        } else {
          return errno_copy;
        }
      } else {
        /* No Error */
        return 0;
      }
    } else {
      /* Have the FULL Stack which supports SO_ERROR */
      /* Hasn't been written yet, never been tested! */
      /* Jerry.Kirk@Nexwarecorp.com */

      int err;
      _PRSockLen_t optlen = sizeof(err);

      if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) == -1) {
        return errno;
      } else {
        return err;
      }
    }
  } else {
    return ECONNREFUSED;
  }
#else
  int err;
  _PRSockLen_t optlen = sizeof(err);
  if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &optlen) == -1) {
    return errno;
  }
  return err;

#endif
}

/************************************************************************/

/*
** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread
** safe.  Unfortunately, neither is mozilla. To make these programs work
** in a pre-emptive threaded environment, we need to use a lock.
*/


void _PR_XLock(void) { PR_EnterMonitor(_pr_Xfe_mon); }

void _PR_XUnlock(void) { PR_ExitMonitor(_pr_Xfe_mon); }

PRBool _PR_XIsLocked(void) {
  return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE;
}

#if defined(HAVE_FCNTL_FILE_LOCKING)

PRStatus _MD_LockFile(PRInt32 f) {
  PRInt32 rv;
  struct flock arg;

  arg.l_type = F_WRLCK;
  arg.l_whence = SEEK_SET;
  arg.l_start = 0;
  arg.l_len = 0; /* until EOF */
  rv = fcntl(f, F_SETLKW, &arg);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_TLockFile(PRInt32 f) {
  PRInt32 rv;
  struct flock arg;

  arg.l_type = F_WRLCK;
  arg.l_whence = SEEK_SET;
  arg.l_start = 0;
  arg.l_len = 0; /* until EOF */
  rv = fcntl(f, F_SETLK, &arg);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_UnlockFile(PRInt32 f) {
  PRInt32 rv;
  struct flock arg;

  arg.l_type = F_UNLCK;
  arg.l_whence = SEEK_SET;
  arg.l_start = 0;
  arg.l_len = 0; /* until EOF */
  rv = fcntl(f, F_SETLK, &arg);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

#elif defined(HAVE_BSD_FLOCK)

#  include <sys/file.h>

PRStatus _MD_LockFile(PRInt32 f) {
  PRInt32 rv;
  rv = flock(f, LOCK_EX);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_TLockFile(PRInt32 f) {
  PRInt32 rv;
  rv = flock(f, LOCK_EX | LOCK_NB);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_UnlockFile(PRInt32 f) {
  PRInt32 rv;
  rv = flock(f, LOCK_UN);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}
#else

PRStatus _MD_LockFile(PRInt32 f) {
  PRInt32 rv;
  rv = lockf(f, F_LOCK, 0);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_TLockFile(PRInt32 f) {
  PRInt32 rv;
  rv = lockf(f, F_TLOCK, 0);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_UnlockFile(PRInt32 f) {
  PRInt32 rv;
  rv = lockf(f, F_ULOCK, 0);
  if (rv == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}
#endif

PRStatus _MD_gethostname(char* name, PRUint32 namelen) {
  PRIntn rv;

  rv = gethostname(name, namelen);
  if (0 == rv) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO());
  return PR_FAILURE;
}

PRStatus _MD_getsysinfo(PRSysInfo cmd, char* name, PRUint32 namelen) {
  struct utsname info;

  PR_ASSERT((cmd == PR_SI_SYSNAME) || (cmd == PR_SI_RELEASE) ||
            (cmd == PR_SI_RELEASE_BUILD));

  if (uname(&info) == -1) {
    _PR_MD_MAP_DEFAULT_ERROR(errno);
    return PR_FAILURE;
  }
  if (PR_SI_SYSNAME == cmd) {
    (void)PR_snprintf(name, namelen, info.sysname);
  } else if (PR_SI_RELEASE == cmd) {
    (void)PR_snprintf(name, namelen, info.release);
  } else if (PR_SI_RELEASE_BUILD == cmd) {
    (void)PR_snprintf(name, namelen, info.version);
  } else {
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

/*
 *******************************************************************
 *
 * Memory-mapped files
 *
 *******************************************************************
 */


PRStatus _MD_CreateFileMap(PRFileMap* fmap, PRInt64 size) {
  PRFileInfo info;
  PRUint32 sz;

  LL_L2UI(sz, size);
  if (sz) {
    if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) {
      return PR_FAILURE;
    }
    if (sz > info.size) {
      /*
       * Need to extend the file
       */

      if (fmap->prot != PR_PROT_READWRITE) {
        PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0);
        return PR_FAILURE;
      }
      if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) {
        return PR_FAILURE;
      }
      if (PR_Write(fmap->fd, "", 1) != 1) {
        return PR_FAILURE;
      }
    }
  }
  if (fmap->prot == PR_PROT_READONLY) {
    fmap->md.prot = PROT_READ;
#if defined(DARWIN) || defined(ANDROID)
    /*
     * This is needed on OS X because its implementation of
     * POSIX shared memory returns an error for MAP_PRIVATE, even
     * when the mapping is read-only.
     *
     * And this is needed on Android, because mapping ashmem with
     * MAP_PRIVATE creates a mapping of zeroed memory instead of
     * the shm contents.
     */

    fmap->md.flags = MAP_SHARED;
#else
    fmap->md.flags = MAP_PRIVATE;
#endif
  } else if (fmap->prot == PR_PROT_READWRITE) {
    fmap->md.prot = PROT_READ | PROT_WRITE;
    fmap->md.flags = MAP_SHARED;
  } else {
    PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
    fmap->md.prot = PROT_READ | PROT_WRITE;
    fmap->md.flags = MAP_PRIVATE;
  }
  return PR_SUCCESS;
}

void* _MD_MemMap(PRFileMap* fmap, PRInt64 offset, PRUint32 len) {
  PRInt32 off;
  void* addr;

  LL_L2I(off, offset);
  if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags,
                   fmap->fd->secret->md.osfd, off)) == (void*)-1) {
    _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO());
    addr = NULL;
  }
  return addr;
}

PRStatus _MD_MemUnmap(void* addr, PRUint32 len) {
  if (munmap(addr, len) == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_DEFAULT_ERROR(errno);
  return PR_FAILURE;
}

PRStatus _MD_CloseFileMap(PRFileMap* fmap) {
  if (PR_TRUE == fmap->md.isAnonFM) {
    PRStatus rc = PR_Close(fmap->fd);
    if (PR_FAILURE == rc) {
      PR_LOG(_pr_io_lm, PR_LOG_DEBUG,
             ("_MD_CloseFileMap(): error closing anonymnous file map osfd"));
      return PR_FAILURE;
    }
  }
  PR_DELETE(fmap);
  return PR_SUCCESS;
}

PRStatus _MD_SyncMemMap(PRFileDesc* fd, void* addr, PRUint32 len) {
  /* msync(..., MS_SYNC) alone is sufficient to flush modified data to disk
   * synchronously. It is not necessary to call fsync. */

  if (msync(addr, len, MS_SYNC) == 0) {
    return PR_SUCCESS;
  }
  _PR_MD_MAP_DEFAULT_ERROR(errno);
  return PR_FAILURE;
}

#if defined(_PR_NEED_FAKE_POLL)

/*
 * Some platforms don't have poll().  For easier porting of code
 * that calls poll(), we emulate poll() using select().
 */


int poll(struct pollfd* filedes, unsigned long nfds, int timeout) {
  int i;
  int rv;
  int maxfd;
  fd_set rd, wr, ex;
  struct timeval tv, *tvp;

  if (timeout < 0 && timeout != -1) {
    errno = EINVAL;
    return -1;
  }

  if (timeout == -1) {
    tvp = NULL;
  } else {
    tv.tv_sec = timeout / 1000;
    tv.tv_usec = (timeout % 1000) * 1000;
    tvp = &tv;
  }

  maxfd = -1;
  FD_ZERO(&rd);
  FD_ZERO(&wr);
  FD_ZERO(&ex);

  for (i = 0; i < nfds; i++) {
    int osfd = filedes[i].fd;
    int events = filedes[i].events;
    PRBool fdHasEvent = PR_FALSE;

    PR_ASSERT(osfd < FD_SETSIZE);
    if (osfd < 0 || osfd >= FD_SETSIZE) {
      continue/* Skip this osfd. */
    }

    /*
     * Map the poll events to 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.
     */


    if (events & (POLLIN | POLLRDNORM)) {
      FD_SET(osfd, &rd);
      fdHasEvent = PR_TRUE;
    }
    if (events & (POLLOUT | POLLWRNORM)) {
      FD_SET(osfd, &wr);
      fdHasEvent = PR_TRUE;
    }
    if (events & (POLLPRI | POLLRDBAND)) {
      FD_SET(osfd, &ex);
      fdHasEvent = PR_TRUE;
    }
    if (fdHasEvent && osfd > maxfd) {
      maxfd = osfd;
    }
  }

  rv = select(maxfd + 1, &rd, &wr, &ex, tvp);

  /* Compute poll results */
  if (rv > 0) {
    rv = 0;
    for (i = 0; i < nfds; i++) {
      PRBool fdHasEvent = PR_FALSE;

      filedes[i].revents = 0;
      if (filedes[i].fd < 0) {
        continue;
      }
      if (filedes[i].fd >= FD_SETSIZE) {
        filedes[i].revents |= POLLNVAL;
        continue;
      }
      if (FD_ISSET(filedes[i].fd, &rd)) {
        if (filedes[i].events & POLLIN) {
          filedes[i].revents |= POLLIN;
        }
        if (filedes[i].events & POLLRDNORM) {
          filedes[i].revents |= POLLRDNORM;
        }
        fdHasEvent = PR_TRUE;
      }
      if (FD_ISSET(filedes[i].fd, &wr)) {
        if (filedes[i].events & POLLOUT) {
          filedes[i].revents |= POLLOUT;
        }
        if (filedes[i].events & POLLWRNORM) {
          filedes[i].revents |= POLLWRNORM;
        }
        fdHasEvent = PR_TRUE;
      }
      if (FD_ISSET(filedes[i].fd, &ex)) {
        if (filedes[i].events & POLLPRI) {
          filedes[i].revents |= POLLPRI;
        }
        if (filedes[i].events & POLLRDBAND) {
          filedes[i].revents |= POLLRDBAND;
        }
        fdHasEvent = PR_TRUE;
      }
      if (fdHasEvent) {
        rv++;
      }
    }
    PR_ASSERT(rv > 0);
  } else if (rv == -1 && errno == EBADF) {
    rv = 0;
    for (i = 0; i < nfds; i++) {
      filedes[i].revents = 0;
      if (filedes[i].fd < 0) {
        continue;
      }
      if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) {
        filedes[i].revents = POLLNVAL;
        rv++;
      }
    }
    PR_ASSERT(rv > 0);
  }
  PR_ASSERT(-1 != timeout || rv != 0);

  return rv;
}
#endif /* _PR_NEED_FAKE_POLL */

Messung V0.5 in Prozent
C=95 H=88 G=91

¤ Dauer der Verarbeitung: 0.42 Sekunden  (vorverarbeitet am  2026-04-28) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.