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

/* Windows NT IO module
 *
 * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
 * For LOCAL_SCOPE threads, we're using NT fibers.  For GLOBAL_SCOPE threads
 * we're using NT-native threads.
 *
 * When doing IO, we want to use completion ports for optimal performance
 * with fibers.  But if we use completion ports for all IO, it is difficult
 * to project a blocking model with GLOBAL_SCOPE threads.  To handle this
 * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
 * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
 * threads because it means extra context switches, which are really slow
 * on NT...  Since we're using a single completion port, some IO will
 * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
 * extra context switching; but I don't think there is anything I can do
 * about it.
 */

#include "primpl.h"
#include "pprmwait.h"
#include <direct.h>
#include <mbstring.h>

static HANDLE _pr_completion_port;
static PRThread* _pr_io_completion_thread;

#define RECYCLE_SIZE 512
static struct _MDLock _pr_recycle_lock;
static PRInt32 _pr_recycle_INET_array[RECYCLE_SIZE];
static PRInt32 _pr_recycle_INET_tail = 0;
static PRInt32 _pr_recycle_INET6_array[RECYCLE_SIZE];
static PRInt32 _pr_recycle_INET6_tail = 0;

__declspec(thread) PRThread* _pr_io_restarted_io = NULL;
DWORD _pr_io_restartedIOIndex; /* The thread local storage slot for each
                                * thread is initialized to NULL. */

PRBool _nt_version_gets_lockfile_completion;

struct _MDLock _pr_ioq_lock;
extern _MDLock _nt_idleLock;
extern PRCList _nt_idleList;
extern PRUint32 _nt_idleCount;

#define CLOSE_TIMEOUT PR_SecondsToInterval(5)

/*
 * NSPR-to-NT access right mapping table for files.
 */
static DWORD fileAccessTable[] = {FILE_GENERIC_READ, FILE_GENERIC_WRITE,
                                  FILE_GENERIC_EXECUTE};

/*
 * NSPR-to-NT access right mapping table for directories.
 */
static DWORD dirAccessTable[] = {FILE_GENERIC_READ,
                                 FILE_GENERIC_WRITE | FILE_DELETE_CHILD,
                                 FILE_GENERIC_EXECUTE};

static PRBool IsPrevCharSlash(const char* str, const char* current);

#define _NEED_351_FILE_LOCKING_HACK
#ifdef _NEED_351_FILE_LOCKING_HACK
#  define _PR_LOCAL_FILE 1
#  define _PR_REMOTE_FILE 2
PRBool IsFileLocalInit();
PRInt32 IsFileLocal(HANDLE hFile);
#endif /* _NEED_351_FILE_LOCKING_HACK */

static PRInt32 _md_MakeNonblock(HANDLE);

static PROsfd _nt_nonblock_accept(PRFileDesc* fd, struct sockaddr* addr,
                                  int* addrlen, PRIntervalTime);
static PRInt32 _nt_nonblock_connect(PRFileDesc* fd, struct sockaddr* addr,
                                    int addrlen, PRIntervalTime);
static PRInt32 _nt_nonblock_recv(PRFileDesc* fd, char* buf, int len, int flags,
                                 PRIntervalTime);
static PRInt32 _nt_nonblock_send(PRFileDesc* fd, char* buf, int len,
                                 PRIntervalTime);
static PRInt32 _nt_nonblock_writev(PRFileDesc* fd, const PRIOVec* iov, int size,
                                   PRIntervalTime);
static PRInt32 _nt_nonblock_sendto(PRFileDesc*, const char*, int,
                                   const struct sockaddr*, int, PRIntervalTime);
static PRInt32 _nt_nonblock_recvfrom(PRFileDesc*, char*, int, struct sockaddr*,
                                     int*, PRIntervalTime);

/*
 * We cannot associate a fd (a socket) with an I/O completion port
 * if the fd is nonblocking or inheritable.
 *
 * Nonblocking socket I/O won't work if the socket is associated with
 * an I/O completion port.
 *
 * An inheritable fd cannot be associated with an I/O completion port
 * because the completion notification of async I/O initiated by the
 * child process is still posted to the I/O completion port in the
 * parent process.
 */
#define _NT_USE_NB_IO(fd) \
  ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE)

/*
 * UDP support
 *
 * UDP is supported on NT by the continuation thread mechanism.
 * The code is borrowed from ptio.c in pthreads nspr, hence the
 * PT and pt prefixes.  This mechanism is in fact general and
 * not limited to UDP.  For now, only UDP's recvfrom and sendto
 * go through the continuation thread if they get WSAEWOULDBLOCK
 * on first try.  Recv and send on a connected UDP socket still
 * goes through asychronous io.
 */

#define PT_DEFAULT_SELECT_MSEC 100

typedef struct pt_Continuation pt_Continuation;
typedef PRBool (*ContinuationFn)(pt_Continuation* op, PRInt16 revent);

typedef enum pr_ContuationStatus {
  pt_continuation_sumbitted,
  pt_continuation_inprogress,
  pt_continuation_abort,
  pt_continuation_done
} pr_ContuationStatus;

struct pt_Continuation {
  /* These objects are linked in ascending timeout order */
  pt_Continuation *next, *prev; /* self linked list of these things */

  /* The building of the continuation operation */
  ContinuationFn function; /* what function to continue */
  union {
    SOCKET osfd;
  } arg1; /* #1 - the op's fd */
  union {
    void* buffer;
  } arg2; /* #2 - primary transfer buffer */
  union {
    PRIntn amount;
  } arg3; /* #3 - size of 'buffer' */
  union {
    PRIntn flags;
  } arg4; /* #4 - read/write flags */
  union {
    PRNetAddr* addr;
  } arg5; /* #5 - send/recv address */

  PRIntervalTime timeout; /* representation of the timeout */

  PRIntn event; /* flags for select()'s events */

  /*
  ** The representation and notification of the results of the operation.
  ** These function can either return an int return code or a pointer to
  ** some object.
  */
  union {
    PRIntn code;
    void* object;
  } result;

  PRIntn syserrno;            /* in case it failed, why (errno) */
  pr_ContuationStatus status; /* the status of the operation */
  PRCondVar* complete;        /* to notify the initiating thread */
};

static struct pt_TimedQueue {
  PRLock* ml;                   /* a little protection */
  PRThread* thread;             /* internal thread's identification */
  PRCondVar* new_op;            /* new operation supplied */
  PRCondVar* finish_op;         /* an existing operation finished */
  PRUintn op_count;             /* number of operations in the list */
  pt_Continuation *head, *tail; /* head/tail of list of operations */

  pt_Continuation* op;  /* timed operation furthest in future */
  PRIntervalTime epoch; /* the epoch of 'timed' */
} pt_tq;

#if defined(DEBUG)
static struct pt_debug_s {
  PRIntn predictionsFoiled;
  PRIntn pollingListMax;
  PRIntn continuationsServed;
} pt_debug;
#endif /* DEBUG */

static void ContinuationThread(void* arg);
static PRInt32 pt_SendTo(SOCKET osfd, const void* buf, PRInt32 amount,
                         PRInt32 flags, const PRNetAddr* addr, PRIntn addrlen,
                         PRIntervalTime timeout);
static PRInt32 pt_RecvFrom(SOCKET osfd, void* buf, PRInt32 amount,
                           PRInt32 flags, PRNetAddr* addr, PRIntn* addr_len,
                           PRIntervalTime timeout);

/* The key returned from GetQueuedCompletionStatus() is used to determine what
 * type of completion we have.  We differentiate between IO completions and
 * CVAR completions.
 */
#define KEY_IO 0xaaaaaaaa
#define KEY_CVAR 0xbbbbbbbb

PRInt32 _PR_MD_PAUSE_CPU(PRIntervalTime ticks) {
  int awoken = 0;
  unsigned long bytes, key;
  int rv;
  LPOVERLAPPED olp;
  _MDOverlapped* mdOlp;
  PRUint32 timeout;

  if (_nt_idleCount > 0) {
    PRThread* deadThread;

    _MD_LOCK(&_nt_idleLock);
    while (!PR_CLIST_IS_EMPTY(&_nt_idleList)) {
      deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
      PR_REMOVE_LINK(&deadThread->links);

      PR_ASSERT(deadThread->state == _PR_DEAD_STATE);

      /* XXXMB - cleanup to do here? */
      if (!_PR_IS_NATIVE_THREAD(deadThread)) {
        /* Spinlock while user thread is still running.
         * There is no way to use a condition variable here. The thread
         * is dead, and we have to wait until we switch off the dead
         * thread before we can kill the fiber completely.
         */
        while (deadThread->no_sched);

        DeleteFiber(deadThread->md.fiber_id);
      }
      memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
      if (!deadThread->threadAllocatedOnStack) {
        PR_DELETE(deadThread);
      }
      _nt_idleCount--;
    }
    _MD_UNLOCK(&_nt_idleLock);
  }

  if (ticks == PR_INTERVAL_NO_TIMEOUT)
#if 0
        timeout = INFINITE;
#else
    /*
     * temporary hack to poll the runq every 5 seconds because of bug in
     * native threads creating user threads and not poking the right cpu.
     *
     * A local thread that was interrupted is bound to its current
     * cpu but there is no easy way for the interrupter to poke the
     * right cpu.  This is a hack to poll the runq every 5 seconds.
     */
    timeout = 5000;
#endif
  else {
    timeout = PR_IntervalToMilliseconds(ticks);
  }

  /*
   * The idea of looping here is to complete as many IOs as possible before
   * returning.  This should minimize trips to the idle thread.
   */
  while (1) {
    rv = GetQueuedCompletionStatus(_pr_completion_port, &bytes, &key, &olp,
                                   timeout);
    if (rv == 0 && olp == NULL) {
      /* Error in GetQueuedCompetionStatus */
      if (GetLastError() != WAIT_TIMEOUT) {
        /* ARGH - what can we do here? Log an error? XXXMB */
        return -1;
      } else {
        /* If awoken == 0, then we just had a timeout */
        return awoken;
      }
    }

    if (olp == NULL) {
      return 0;
    }

    mdOlp = (_MDOverlapped*)olp;

    if (mdOlp->ioModel == _MD_MultiWaitIO) {
      PRRecvWait* desc;
      PRWaitGroup* group;
      PRThread* thred = NULL;
      PRMWStatus mwstatus;

      desc = mdOlp->data.mw.desc;
      PR_ASSERT(desc != NULL);
      mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
      if (InterlockedCompareExchange((PVOID*)&desc->outcome, (PVOID)mwstatus,
                                     (PVOID)PR_MW_PENDING) ==
          (PVOID)PR_MW_PENDING) {
        if (mwstatus == PR_MW_SUCCESS) {
          desc->bytesRecv = bytes;
        } else {
          mdOlp->data.mw.error = GetLastError();
        }
      }
      group = mdOlp->data.mw.group;
      PR_ASSERT(group != NULL);

      _PR_MD_LOCK(&group->mdlock);
      PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
      PR_ASSERT(desc->fd != NULL);
      NT_HashRemoveInternal(group, desc->fd);
      if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
        thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
        PR_REMOVE_LINK(&thred->waitQLinks);
      }
      _PR_MD_UNLOCK(&group->mdlock);

      if (thred) {
        if (!_PR_IS_NATIVE_THREAD(thred)) {
          int pri = thred->priority;
          _PRCPU* lockedCPU = _PR_MD_CURRENT_CPU();
          _PR_THREAD_LOCK(thred);
          if (thred->flags & _PR_ON_PAUSEQ) {
            _PR_SLEEPQ_LOCK(thred->cpu);
            _PR_DEL_SLEEPQ(thred, PR_TRUE);
            _PR_SLEEPQ_UNLOCK(thred->cpu);
            _PR_THREAD_UNLOCK(thred);
            thred->cpu = lockedCPU;
            thred->state = _PR_RUNNABLE;
            _PR_RUNQ_LOCK(lockedCPU);
            _PR_ADD_RUNQ(thred, lockedCPU, pri);
            _PR_RUNQ_UNLOCK(lockedCPU);
          } else {
            /*
             * The thread was just interrupted and moved
             * from the pause queue to the run queue.
             */
            _PR_THREAD_UNLOCK(thred);
          }
        } else {
          _PR_THREAD_LOCK(thred);
          thred->state = _PR_RUNNABLE;
          _PR_THREAD_UNLOCK(thred);
          ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
        }
      }
    } else {
      PRThread* completed_io;

      PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
      completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
      completed_io->md.blocked_io_status = rv;
      if (rv == 0) {
        completed_io->md.blocked_io_error = GetLastError();
      }
      completed_io->md.blocked_io_bytes = bytes;

      if (!_PR_IS_NATIVE_THREAD(completed_io)) {
        int pri = completed_io->priority;
        _PRCPU* lockedCPU = _PR_MD_CURRENT_CPU();

        /* The KEY_CVAR notification only occurs when a native thread
         * is notifying a user thread.  For user-user notifications
         * the wakeup occurs by having the notifier place the thread
         * on the runq directly; for native-native notifications the
         * wakeup occurs by calling ReleaseSemaphore.
         */
        if (key == KEY_CVAR) {
          PR_ASSERT(completed_io->io_pending == PR_FALSE);
          PR_ASSERT(completed_io->io_suspended == PR_FALSE);
          PR_ASSERT(completed_io->md.thr_bound_cpu == NULL);

          /* Thread has already been deleted from sleepQ */

          /* Switch CPU and add to runQ */
          completed_io->cpu = lockedCPU;
          completed_io->state = _PR_RUNNABLE;
          _PR_RUNQ_LOCK(lockedCPU);
          _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
          _PR_RUNQ_UNLOCK(lockedCPU);
        } else {
          PR_ASSERT(key == KEY_IO);
          PR_ASSERT(completed_io->io_pending == PR_TRUE);

          _PR_THREAD_LOCK(completed_io);

          completed_io->io_pending = PR_FALSE;

          /* If io_suspended is true, then this IO has already resumed.
           * We don't need to do anything; because the thread is
           * already running.
           */
          if (completed_io->io_suspended == PR_FALSE) {
            if (completed_io->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ)) {
              _PR_SLEEPQ_LOCK(completed_io->cpu);
              _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
              _PR_SLEEPQ_UNLOCK(completed_io->cpu);

              _PR_THREAD_UNLOCK(completed_io);

              /*
               * If an I/O operation is suspended, the thread
               * must be running on the same cpu on which the
               * I/O operation was issued.
               */
              PR_ASSERT(!completed_io->md.thr_bound_cpu ||
                        (completed_io->cpu == completed_io->md.thr_bound_cpu));

              if (!completed_io->md.thr_bound_cpu) {
                completed_io->cpu = lockedCPU;
              }
              completed_io->state = _PR_RUNNABLE;
              _PR_RUNQ_LOCK(completed_io->cpu);
              _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri);
              _PR_RUNQ_UNLOCK(completed_io->cpu);
            } else {
              _PR_THREAD_UNLOCK(completed_io);
            }
          } else {
            _PR_THREAD_UNLOCK(completed_io);
          }
        }
      } else {
        /* For native threads, they are only notified through this loop
         * when completing IO.  So, don't worry about this being a CVAR
         * notification, because that is not possible.
         */
        _PR_THREAD_LOCK(completed_io);
        completed_io->io_pending = PR_FALSE;
        if (completed_io->io_suspended == PR_FALSE) {
          completed_io->state = _PR_RUNNABLE;
          _PR_THREAD_UNLOCK(completed_io);
          rv = ReleaseSemaphore(completed_io->md.blocked_sema, 1, NULL);
          PR_ASSERT(0 != rv);
        } else {
          _PR_THREAD_UNLOCK(completed_io);
        }
      }
    }

    awoken++;
    timeout = 0; /* Don't block on subsequent trips through the loop */
  }

  /* never reached */
  return 0;
}

static PRStatus _native_thread_md_wait(PRThread* thread, PRIntervalTime ticks) {
  DWORD rv;
  PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT)
                       ? INFINITE
                       : PR_IntervalToMilliseconds(ticks);

  /*
   * thread waiting for a cvar or a joining thread
   */
  rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
  switch (rv) {
    case WAIT_OBJECT_0:
      return PR_SUCCESS;
      break;
    case WAIT_TIMEOUT:
      _PR_THREAD_LOCK(thread);
      PR_ASSERT(thread->state != _PR_IO_WAIT);
      if (thread->wait.cvar != NULL) {
        PR_ASSERT(thread->state == _PR_COND_WAIT);
        thread->wait.cvar = NULL;
        thread->state = _PR_RUNNING;
        _PR_THREAD_UNLOCK(thread);
      } else {
        /* The CVAR was notified just as the timeout
         * occurred.  This left the semaphore in the
         * signaled state.  Call WaitForSingleObject()
         * to clear the semaphore.
         */
        _PR_THREAD_UNLOCK(thread);
        rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
        PR_ASSERT(rv == WAIT_OBJECT_0);
      }
      return PR_SUCCESS;
      break;
    default:
      return PR_FAILURE;
      break;
  }

  return PR_SUCCESS;
}

PRStatus _PR_MD_WAIT(PRThread* thread, PRIntervalTime ticks) {
  DWORD rv;

  if (_native_threads_only) {
    return (_native_thread_md_wait(thread, ticks));
  }
  if (thread->flags & _PR_GLOBAL_SCOPE) {
    PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT)
                         ? INFINITE
                         : PR_IntervalToMilliseconds(ticks);
    rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
    switch (rv) {
      case WAIT_OBJECT_0:
        return PR_SUCCESS;
        break;
      case WAIT_TIMEOUT:
        _PR_THREAD_LOCK(thread);
        if (thread->state == _PR_IO_WAIT) {
          if (thread->io_pending == PR_TRUE) {
            thread->state = _PR_RUNNING;
            thread->io_suspended = PR_TRUE;
            _PR_THREAD_UNLOCK(thread);
          } else {
            /* The IO completed just at the same time the timeout
             * occurred.  This left the semaphore in the signaled
             * state.  Call WaitForSingleObject() to clear the
             * semaphore.
             */
            _PR_THREAD_UNLOCK(thread);
            rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
            PR_ASSERT(rv == WAIT_OBJECT_0);
          }
        } else {
          if (thread->wait.cvar != NULL) {
            PR_ASSERT(thread->state == _PR_COND_WAIT);
            thread->wait.cvar = NULL;
            thread->state = _PR_RUNNING;
            _PR_THREAD_UNLOCK(thread);
          } else {
            /* The CVAR was notified just as the timeout
             * occurred.  This left the semaphore in the
             * signaled state.  Call WaitForSingleObject()
             * to clear the semaphore.
             */
            _PR_THREAD_UNLOCK(thread);
            rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
            PR_ASSERT(rv == WAIT_OBJECT_0);
          }
        }
        return PR_SUCCESS;
        break;
      default:
        return PR_FAILURE;
        break;
    }
  } else {
    PRInt32 is;

    _PR_INTSOFF(is);
    _PR_MD_SWITCH_CONTEXT(thread);
  }

  return PR_SUCCESS;
}

static void _native_thread_io_nowait(PRThread* thread, int rv, int bytes) {
  int rc;

  PR_ASSERT(rv != 0);
  _PR_THREAD_LOCK(thread);
  if (thread->state == _PR_IO_WAIT) {
    PR_ASSERT(thread->io_suspended == PR_FALSE);
    PR_ASSERT(thread->io_pending == PR_TRUE);
    thread->state = _PR_RUNNING;
    thread->io_pending = PR_FALSE;
    _PR_THREAD_UNLOCK(thread);
  } else {
    /* The IO completed just at the same time the
     * thread was interrupted. This left the semaphore
     * in the signaled state. Call WaitForSingleObject()
     * to clear the semaphore.
     */
    PR_ASSERT(thread->io_suspended == PR_TRUE);
    PR_ASSERT(thread->io_pending == PR_TRUE);
    thread->io_pending = PR_FALSE;
    _PR_THREAD_UNLOCK(thread);
    rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
    PR_ASSERT(rc == WAIT_OBJECT_0);
  }

  thread->md.blocked_io_status = rv;
  thread->md.blocked_io_bytes = bytes;
  rc = ResetEvent(thread->md.thr_event);
  PR_ASSERT(rc != 0);
  return;
}

static PRStatus _native_thread_io_wait(PRThread* thread, PRIntervalTime ticks) {
  DWORD rv, bytes;
#define _NATIVE_IO_WAIT_HANDLES 2
#define _NATIVE_WAKEUP_EVENT_INDEX 0
#define _NATIVE_IO_EVENT_INDEX 1

  HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES];

  PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT)
                       ? INFINITE
                       : PR_IntervalToMilliseconds(ticks);

  PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE);

  wait_handles[0] = thread->md.blocked_sema;
  wait_handles[1] = thread->md.thr_event;
  rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles, FALSE,
                              msecs);

  switch (rv) {
    case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX:
      /*
       * I/O op completed
       */
      _PR_THREAD_LOCK(thread);
      if (thread->state == _PR_IO_WAIT) {
        PR_ASSERT(thread->io_suspended == PR_FALSE);
        PR_ASSERT(thread->io_pending == PR_TRUE);
        thread->state = _PR_RUNNING;
        thread->io_pending = PR_FALSE;
        _PR_THREAD_UNLOCK(thread);
      } else {
        /* The IO completed just at the same time the
         * thread was interrupted. This led to us being
         * notified twice. Call WaitForSingleObject()
         * to clear the semaphore.
         */
        PR_ASSERT(thread->io_suspended == PR_TRUE);
        PR_ASSERT(thread->io_pending == PR_TRUE);
        thread->io_pending = PR_FALSE;
        _PR_THREAD_UNLOCK(thread);
        rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
        PR_ASSERT(rv == WAIT_OBJECT_0);
      }

      rv =
          GetOverlappedResult((HANDLE)thread->io_fd,
                              &thread->md.overlapped.overlapped, &bytes, FALSE);

      thread->md.blocked_io_status = rv;
      if (rv != 0) {
        thread->md.blocked_io_bytes = bytes;
      } else {
        thread->md.blocked_io_error = GetLastError();
        PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error);
      }
      rv = ResetEvent(thread->md.thr_event);
      PR_ASSERT(rv != 0);
      break;
    case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX:
      /*
       * I/O interrupted;
       */
#ifdef DEBUG
      _PR_THREAD_LOCK(thread);
      PR_ASSERT(thread->io_suspended == PR_TRUE);
      _PR_THREAD_UNLOCK(thread);
#endif
      break;
    case WAIT_TIMEOUT:
      _PR_THREAD_LOCK(thread);
      if (thread->state == _PR_IO_WAIT) {
        thread->state = _PR_RUNNING;
        thread->io_suspended = PR_TRUE;
        _PR_THREAD_UNLOCK(thread);
      } else {
        /*
         * The thread was interrupted just as the timeout
         * occurred. This left the semaphore in the signaled
         * state. Call WaitForSingleObject() to clear the
         * semaphore.
         */
        PR_ASSERT(thread->io_suspended == PR_TRUE);
        _PR_THREAD_UNLOCK(thread);
        rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
        PR_ASSERT(rv == WAIT_OBJECT_0);
      }
      break;
    default:
      return PR_FAILURE;
      break;
  }

  return PR_SUCCESS;
}

static PRStatus _NT_IO_WAIT(PRThread* thread, PRIntervalTime timeout) {
  PRBool fWait = PR_TRUE;

  if (_native_threads_only) {
    return (_native_thread_io_wait(thread, timeout));
  }
  if (!_PR_IS_NATIVE_THREAD(thread)) {
    _PR_THREAD_LOCK(thread);

    /* The IO may have already completed; if so, don't add to sleepQ,
     * since we are already on the runQ!
     */
    if (thread->io_pending == PR_TRUE) {
      _PR_SLEEPQ_LOCK(thread->cpu);
      _PR_ADD_SLEEPQ(thread, timeout);
      _PR_SLEEPQ_UNLOCK(thread->cpu);
    } else {
      fWait = PR_FALSE;
    }
    _PR_THREAD_UNLOCK(thread);
  }
  if (fWait) {
    return _PR_MD_WAIT(thread, timeout);
  } else {
    return PR_SUCCESS;
  }
}

/*
 * 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) {
  PRStatus rv;
  _PRCPU* cpu = thr->cpu;

  PR_ASSERT(thr->state == _PR_IO_WAIT);
  /*
   * A thread for which an I/O timed out or was interrupted cannot be
   * in an IO_WAIT state except as a result of calling PR_Close or
   * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state
   * is not interruptible
   */
  if (thr->md.interrupt_disabled == PR_TRUE) {
    _PR_THREAD_UNLOCK(thr);
    return;
  }
  thr->io_suspended = PR_TRUE;
  thr->state = _PR_RUNNABLE;

  if (!_PR_IS_NATIVE_THREAD(thr)) {
    PRThread* me = _PR_MD_CURRENT_THREAD();
    PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
    _PR_SLEEPQ_LOCK(cpu);
    _PR_DEL_SLEEPQ(thr, PR_TRUE);
    _PR_SLEEPQ_UNLOCK(cpu);
    /*
     * this thread will continue to run on the same cpu until the
     * I/O is aborted by closing the FD or calling CancelIO
     */
    thr->md.thr_bound_cpu = cpu;

    PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
    _PR_AddThreadToRunQ(me, thr);
  }
  _PR_THREAD_UNLOCK(thr);
  rv = _PR_MD_WAKEUP_WAITER(thr);
  PR_ASSERT(PR_SUCCESS == rv);
}

/* Resume an outstanding IO; requires that after the switch, we disable */
static PRStatus _NT_ResumeIO(PRThread* thread, PRIntervalTime ticks) {
  PRBool fWait = PR_TRUE;

  if (!_PR_IS_NATIVE_THREAD(thread)) {
    if (_pr_use_static_tls) {
      _pr_io_restarted_io = thread;
    } else {
      TlsSetValue(_pr_io_restartedIOIndex, thread);
    }
  } else {
    _PR_THREAD_LOCK(thread);
    if (!thread->io_pending) {
      fWait = PR_FALSE;
    }
    thread->io_suspended = PR_FALSE;

    _PR_THREAD_UNLOCK(thread);
  }
  /* We don't put ourselves back on the sleepQ yet; until we
   * set the suspended bit to false, we can't do that.  Just save
   * the sleep time here, and then continue.  The restarted_io handler
   * will add us to the sleepQ if needed.
   */
  thread->sleep = ticks;

  if (fWait) {
    if (!_PR_IS_NATIVE_THREAD(thread)) {
      return _PR_MD_WAIT(thread, ticks);
    } else {
      return _NT_IO_WAIT(thread, ticks);
    }
  }
  return PR_SUCCESS;
}

PRStatus _PR_MD_WAKEUP_WAITER(PRThread* thread) {
  if (thread == NULL) {
    /* If thread is NULL, we aren't waking a thread, we're just poking
     * idle thread
     */
    if (PostQueuedCompletionStatus(_pr_completion_port, 0, KEY_CVAR, NULL) ==
        FALSE) {
      return PR_FAILURE;
    }
    return PR_SUCCESS;
  }

  if (_PR_IS_NATIVE_THREAD(thread)) {
    if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE) {
      return PR_FAILURE;
    } else {
      return PR_SUCCESS;
    }
  } else {
    PRThread* me = _PR_MD_CURRENT_THREAD();

    /* When a Native thread has to awaken a user thread, it has to poke
     * the completion port because all user threads might be idle, and
     * thus the CPUs are just waiting for a completion.
     *
     * XXXMB - can we know when we are truely idle (and not checking
     *         the runq)?
     */
    if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) &&
        (!thread->md.thr_bound_cpu)) {
      /* The thread should not be in any queue */
      PR_ASSERT(thread->queueCount == 0);
      if (PostQueuedCompletionStatus(_pr_completion_port, 0, KEY_CVAR,
                                     &(thread->md.overlapped.overlapped)) ==
          FALSE) {
        return PR_FAILURE;
      }
    }
    return PR_SUCCESS;
  }
}

void _PR_MD_INIT_IO() {
  WORD WSAVersion = 0x0101;
  WSADATA WSAData;
  int err;
  OSVERSIONINFO OSversion;

  err = WSAStartup(WSAVersion, &WSAData);
  PR_ASSERT(0 == err);

  _pr_completion_port =
      CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

  _MD_NEW_LOCK(&_pr_recycle_lock);
  _MD_NEW_LOCK(&_pr_ioq_lock);

  OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  if (GetVersionEx(&OSversion)) {
    _nt_version_gets_lockfile_completion = PR_FALSE;
    if (OSversion.dwMajorVersion >= 4) {
      _nt_version_gets_lockfile_completion = PR_TRUE;
    }
  } else {
    PR_ASSERT(0);
  }

#ifdef _NEED_351_FILE_LOCKING_HACK
  IsFileLocalInit();
#endif /* _NEED_351_FILE_LOCKING_HACK */

  /*
   * UDP support: start up the continuation thread
   */

  pt_tq.op_count = 0;
  pt_tq.head = pt_tq.tail = NULL;
  pt_tq.ml = PR_NewLock();
  PR_ASSERT(NULL != pt_tq.ml);
  pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
  PR_ASSERT(NULL != pt_tq.new_op);
#if defined(DEBUG)
  memset(&pt_debug, 0, sizeof(struct pt_debug_s));
#endif

  pt_tq.thread = PR_CreateThread(PR_SYSTEM_THREAD, ContinuationThread, NULL,
                                 PR_PRIORITY_URGENT, PR_GLOBAL_THREAD,
                                 PR_JOINABLE_THREAD, 0);

  PR_ASSERT(NULL != pt_tq.thread);

#ifdef DEBUG
  /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
  {
    SYSTEMTIME systime;
    union {
      PRTime prt;
      FILETIME ft;
    } filetime;
    BOOL rv;

    systime.wYear = 1970;
    systime.wMonth = 1;
    /* wDayOfWeek is ignored */
    systime.wDay = 1;
    systime.wHour = 0;
    systime.wMinute = 0;
    systime.wSecond = 0;
    systime.wMilliseconds = 0;

    rv = SystemTimeToFileTime(&systime, &filetime.ft);
    PR_ASSERT(0 != rv);
    PR_ASSERT(filetime.prt == _pr_filetime_offset);
  }
#endif /* DEBUG */

  _PR_NT_InitSids();
}

/* --- SOCKET IO --------------------------------------------------------- */

/* _md_get_recycled_socket()
 * Get a socket from the recycle bin; if no sockets are in the bin,
 * create one.  The socket will be passed to AcceptEx() as the
 * second argument.
 */
static SOCKET _md_get_recycled_socket(int af) {
  SOCKET rv;

  _MD_LOCK(&_pr_recycle_lock);
  if (af == AF_INET && _pr_recycle_INET_tail) {
    _pr_recycle_INET_tail--;
    rv = _pr_recycle_INET_array[_pr_recycle_INET_tail];
    _MD_UNLOCK(&_pr_recycle_lock);
    return rv;
  }
  if (af == AF_INET6 && _pr_recycle_INET6_tail) {
    _pr_recycle_INET6_tail--;
    rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail];
    _MD_UNLOCK(&_pr_recycle_lock);
    return rv;
  }
  _MD_UNLOCK(&_pr_recycle_lock);

  rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
  if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
    closesocket(rv);
    return INVALID_SOCKET;
  }
  return rv;
}

/* _md_put_recycled_socket()
 * Add a socket to the recycle bin.
 */
static void _md_put_recycled_socket(SOCKET newsock, int af) {
  PR_ASSERT(_pr_recycle_INET_tail >= 0);
  PR_ASSERT(_pr_recycle_INET6_tail >= 0);

  _MD_LOCK(&_pr_recycle_lock);
  if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) {
    _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock;
    _pr_recycle_INET_tail++;
    _MD_UNLOCK(&_pr_recycle_lock);
  } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) {
    _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock;
    _pr_recycle_INET6_tail++;
    _MD_UNLOCK(&_pr_recycle_lock);
  } else {
    _MD_UNLOCK(&_pr_recycle_lock);
    closesocket(newsock);
  }

  return;
}

/* _md_Associate()
 * Associates a file with the completion port.
 * Returns 0 on failure, 1 on success.
 */
PRInt32 _md_Associate(HANDLE file) {
  HANDLE port;

  if (!_native_threads_only) {
    port = CreateIoCompletionPort((HANDLE)file, _pr_completion_port, KEY_IO, 0);

    /* XXX should map error codes on failures */
    return (port == _pr_completion_port);
  } else {
    return 1;
  }
}

/*
 * _md_MakeNonblock()
 * Make a socket nonblocking.
 * Returns 0 on failure, 1 on success.
 */
static PRInt32 _md_MakeNonblock(HANDLE file) {
  int rv;
  u_long one = 1;

  rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
  /* XXX should map error codes on failures */
  return (rv == 0);
}

static int missing_completions = 0;
static int max_wait_loops = 0;

static PRInt32 _NT_IO_ABORT(PROsfd sock) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRBool fWait;
  PRInt32 rv;
  int loop_count;

  /* This is a clumsy way to abort the IO, but it is all we can do.
   * It looks a bit racy, but we handle all the cases.
   * case 1:  IO completes before calling closesocket
   *     case 1a:  fWait is set to PR_FALSE
   *           This should e the most likely case.  We'll properly
   *           not wait call _NT_IO_WAIT, since the closesocket()
   *           won't be forcing a completion.
   *     case 1b: fWait is set to PR_TRUE
   *           This hopefully won't happen much.  When it does, this
   *           thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
   *           before cleaning up.
   * case 2:  IO does not complete before calling closesocket
   *     case 2a: IO never completes
   *           This is the likely case.  We'll close it and wait
   *           for the completion forced by the close.  Return should
   *           be immediate.
   *     case 2b: IO completes just after calling closesocket
   *           Since the closesocket is issued, we'll either get a
   *           completion back for the real IO or for the close.  We
   *           don't really care.  It may not even be possible to get
   *           a real completion here.  In any event, we'll awaken
   *           from NT_IO_WAIT immediately.
   */

  _PR_THREAD_LOCK(me);
  fWait = me->io_pending;
  if (fWait) {
    /*
     * If there's still I/O pending, it should have already timed
     * out once before this function is called.
     */
    PR_ASSERT(me->io_suspended == PR_TRUE);

    /* Set up to wait for I/O completion again */
    me->state = _PR_IO_WAIT;
    me->io_suspended = PR_FALSE;
    me->md.interrupt_disabled = PR_TRUE;
  }
  _PR_THREAD_UNLOCK(me);

  /* Close the socket if there is one */
  if (sock != INVALID_SOCKET) {
    rv = closesocket((SOCKET)sock);
  }

  /* If there was I/O pending before the close, wait for it to complete */
  if (fWait) {
    /* Wait and wait for the I/O to complete */
    for (loop_count = 0; fWait; ++loop_count) {
      _NT_IO_WAIT(me, CLOSE_TIMEOUT);

      _PR_THREAD_LOCK(me);
      fWait = me->io_pending;
      if (fWait) {
        PR_ASSERT(me->io_suspended == PR_TRUE);
        me->state = _PR_IO_WAIT;
        me->io_suspended = PR_FALSE;
      }
      _PR_THREAD_UNLOCK(me);

      if (loop_count > max_wait_loops) {
        max_wait_loops = loop_count;
      }
    }

    if (loop_count > 1) {
      ++missing_completions;
    }

    me->md.interrupt_disabled = PR_FALSE;
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
  }

  PR_ASSERT(me->io_pending == PR_FALSE);
  me->md.thr_bound_cpu = NULL;
  me->io_suspended = PR_FALSE;

  return rv;
}

PROsfd _PR_MD_SOCKET(int af, int type, int flags) {
  SOCKET sock;

  sock = socket(af, type, flags);

  if (sock == INVALID_SOCKET) {
    _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
  }

  return (PROsfd)sock;
}

struct connect_data_s {
  PRInt32 status;
  PRInt32 error;
  PROsfd osfd;
  struct sockaddr* addr;
  PRUint32 addrlen;
  PRIntervalTime timeout;
};

void _PR_MD_connect_thread(void* cdata) {
  struct connect_data_s* cd = (struct connect_data_s*)cdata;

  cd->status = connect(cd->osfd, cd->addr, cd->addrlen);

  if (cd->status == SOCKET_ERROR) {
    cd->error = WSAGetLastError();
  }

  return;
}

PRInt32 _PR_MD_CONNECT(PRFileDesc* fd, const PRNetAddr* addr, PRUint32 addrlen,
                       PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  u_long nbio;
  PRInt32 rc;

  if (fd->secret->nonblocking) {
    if (!fd->secret->md.io_model_committed) {
      rv = _md_MakeNonblock((HANDLE)osfd);
      PR_ASSERT(0 != rv);
      fd->secret->md.io_model_committed = PR_TRUE;
    }

    if ((rv = connect(osfd, (struct sockaddr*)addr, addrlen)) == -1) {
      err = WSAGetLastError();
      _PR_MD_MAP_CONNECT_ERROR(err);
    }
    return rv;
  }

  /*
   * Temporarily make the socket non-blocking so that we can
   * initiate a non-blocking connect and wait for its completion
   * (with a timeout) in select.
   */
  PR_ASSERT(!fd->secret->md.io_model_committed);
  nbio = 1;
  rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
  PR_ASSERT(0 == rv);

  rc = _nt_nonblock_connect(fd, (struct sockaddr*)addr, addrlen, timeout);

  /* Set the socket back to blocking. */
  nbio = 0;
  rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
  PR_ASSERT(0 == rv);

  return rc;
}

PRInt32 _PR_MD_BIND(PRFileDesc* fd, const PRNetAddr* addr, PRUint32 addrlen) {
  PRInt32 rv;
#if 0
    int one = 1;
#endif

  rv =
      bind(fd->secret->md.osfd, (const struct sockaddr*)&(addr->inet), addrlen);

  if (rv == SOCKET_ERROR) {
    _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
    return -1;
  }

#if 0
    /* Disable nagle- so far unknown if this is good or not...
     */
    rv = setsockopt(fd->secret->md.osfd,
                    SOL_SOCKET,
                    TCP_NODELAY,
                    (const char *)&one,
                    sizeof(one));
    PR_ASSERT(rv == 0);
#endif

  return 0;
}

void _PR_MD_UPDATE_ACCEPT_CONTEXT(PROsfd accept_sock, PROsfd listen_sock) {
  /* Sockets accept()'d with AcceptEx need to call this setsockopt before
   * calling anything other than ReadFile(), WriteFile(), send(), recv(),
   * Transmitfile(), and closesocket().  In order to call any other
   * winsock functions, we have to make this setsockopt call.
   *
   * XXXMB - For the server, we *NEVER* need this in
   * the "normal" code path.  But now we have to call it.  This is a waste
   * of a system call.  We'd like to only call it before calling the
   * obscure socket calls, but since we don't know at that point what the
   * original socket was (or even if it is still alive) we can't do it
   * at that point...
   */
  setsockopt((SOCKET)accept_sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
             (char*)&listen_sock, sizeof(listen_sock));
}

#define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
PROsfd _PR_MD_FAST_ACCEPT(PRFileDesc* fd, PRNetAddr* raddr, PRUint32* rlen,
                          PRIntervalTime timeout, PRBool fast,
                          _PR_AcceptTimeoutCallback callback,
                          void* callbackArg) {
  PROsfd osfd = fd->secret->md.osfd;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  SOCKET accept_sock;
  int bytes;
  PRNetAddr* Laddr;
  PRNetAddr* Raddr;
  PRUint32 llen, err;
  int rv;

  if (_NT_USE_NB_IO(fd)) {
    if (!fd->secret->md.io_model_committed) {
      rv = _md_MakeNonblock((HANDLE)osfd);
      PR_ASSERT(0 != rv);
      fd->secret->md.io_model_committed = PR_TRUE;
    }
    /*
     * The accepted socket inherits the nonblocking and
     * inheritable (HANDLE_FLAG_INHERIT) attributes of
     * the listening socket.
     */
    accept_sock =
        _nt_nonblock_accept(fd, (struct sockaddr*)raddr, rlen, timeout);
    if (!fd->secret->nonblocking) {
      u_long zero = 0;

      rv = ioctlsocket(accept_sock, FIONBIO, &zero);
      PR_ASSERT(0 == rv);
    }
    return accept_sock;
  }

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return -1;
  }

  if (!fd->secret->md.io_model_committed) {
    rv = _md_Associate((HANDLE)osfd);
    PR_ASSERT(0 != rv);
    fd->secret->md.io_model_committed = PR_TRUE;
  }

  if (!me->md.acceptex_buf) {
    me->md.acceptex_buf = PR_MALLOC(2 * INET_ADDR_PADDED);
    if (!me->md.acceptex_buf) {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      return -1;
    }
  }

  accept_sock = _md_get_recycled_socket(fd->secret->af);
  if (accept_sock == INVALID_SOCKET) {
    return -1;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
  if (_native_threads_only) {
    me->md.overlapped.overlapped.hEvent = me->md.thr_event;
  }

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    closesocket(accept_sock);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);
  me->io_fd = osfd;

  rv = AcceptEx((SOCKET)osfd, accept_sock, me->md.acceptex_buf, 0,
                INET_ADDR_PADDED, INET_ADDR_PADDED, &bytes,
                &(me->md.overlapped.overlapped));

  if ((rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING)) {
    /* Argh! The IO failed */
    closesocket(accept_sock);
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return -1;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_ACCEPTEX_ERROR(err);
    return -1;
  }

  if (_native_threads_only && rv) {
    _native_thread_io_nowait(me, rv, bytes);
  } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
    PR_ASSERT(0);
    closesocket(accept_sock);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

  if (me->md.blocked_io_status == 0) {
    closesocket(accept_sock);
    _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
    return -1;
  }

  if (!fast) {
    _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
  }

  /* IO is done */
  GetAcceptExSockaddrs(me->md.acceptex_buf, 0, INET_ADDR_PADDED,
                       INET_ADDR_PADDED, (LPSOCKADDR*)&(Laddr), &llen,
                       (LPSOCKADDR*)&(Raddr), (unsigned int*)rlen);

  if (raddr != NULL) {
    memcpy((char*)raddr, (char*)&Raddr->inet, *rlen);
  }

  PR_ASSERT(me->io_pending == PR_FALSE);

  return accept_sock;
}

PRInt32 _PR_MD_FAST_ACCEPT_READ(PRFileDesc* sd, PROsfd* newSock,
                                PRNetAddr** raddr, void* buf, PRInt32 amount,
                                PRIntervalTime timeout, PRBool fast,
                                _PR_AcceptTimeoutCallback callback,
                                void* callbackArg) {
  PROsfd sock = sd->secret->md.osfd;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  int bytes;
  PRNetAddr* Laddr;
  PRUint32 llen, rlen, err;
  int rv;
  PRBool isConnected;
  PRBool madeCallback = PR_FALSE;

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return -1;
  }

  if (!sd->secret->md.io_model_committed) {
    rv = _md_Associate((HANDLE)sock);
    PR_ASSERT(0 != rv);
    sd->secret->md.io_model_committed = PR_TRUE;
  }

  *newSock = _md_get_recycled_socket(sd->secret->af);
  if (*newSock == INVALID_SOCKET) {
    return -1;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
  if (_native_threads_only) {
    me->md.overlapped.overlapped.hEvent = me->md.thr_event;
  }

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    closesocket(*newSock);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);
  me->io_fd = sock;

  rv = AcceptEx((SOCKET)sock, *newSock, buf, amount, INET_ADDR_PADDED,
                INET_ADDR_PADDED, &bytes, &(me->md.overlapped.overlapped));

  if ((rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
    closesocket(*newSock);
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return -1;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_ACCEPTEX_ERROR(err);
    return -1;
  }

  if (_native_threads_only && rv) {
    _native_thread_io_nowait(me, rv, bytes);
  } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
    PR_ASSERT(0);
    closesocket(*newSock);
    return -1;
  }

retry:
  if (me->io_suspended) {
    PRInt32 err;
    INT seconds;
    INT bytes = sizeof(seconds);

    PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);

    err = getsockopt(*newSock, SOL_SOCKET, SO_CONNECT_TIME, (char*)&seconds,
                     (PINT)&bytes);
    if (err == NO_ERROR) {
      PRIntervalTime elapsed = PR_SecondsToInterval(seconds);

      if (seconds == 0xffffffff) {
        isConnected = PR_FALSE;
      } else {
        isConnected = PR_TRUE;
      }

      if (!isConnected) {
        if (madeCallback == PR_FALSE && callback) {
          callback(callbackArg);
        }
        madeCallback = PR_TRUE;
        me->state = _PR_IO_WAIT;
        if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
          closesocket(*newSock);
          return -1;
        }
        goto retry;
      }

      if (elapsed < timeout) {
        /* Socket is connected but time not elapsed, RESUME IO */
        timeout -= elapsed;
        me->state = _PR_IO_WAIT;
        if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
          closesocket(*newSock);
          return -1;
        }
        goto retry;
      }
    } else {
      /*  What to do here? Assume socket not open?*/
      PR_ASSERT(0);
      isConnected = PR_FALSE;
    }

    rv = _NT_IO_ABORT(*newSock);

    PR_ASSERT(me->io_pending == PR_FALSE);
    PR_ASSERT(me->io_suspended == PR_FALSE);
    PR_ASSERT(me->md.thr_bound_cpu == NULL);
    /* If the IO is still suspended, it means we didn't get any
     * completion from NT_IO_WAIT.  This is not disasterous, I hope,
     * but it may mean we still have an IO outstanding...  Try to
     * recover by just allowing ourselves to continue.
     */
    me->io_suspended = PR_FALSE;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    } else {
      PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
    }
    me->state = _PR_RUNNING;
    closesocket(*newSock);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE);
  PR_ASSERT(me->io_suspended == PR_FALSE);
  PR_ASSERT(me->md.thr_bound_cpu == NULL);

  if (me->md.blocked_io_status == 0) {
    _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
    closesocket(*newSock);
    return -1;
  }

  if (!fast) {
    _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
  }

  /* IO is done */
  GetAcceptExSockaddrs(buf, amount, INET_ADDR_PADDED, INET_ADDR_PADDED,
                       (LPSOCKADDR*)&(Laddr), &llen, (LPSOCKADDR*)(raddr),
                       (unsigned int*)&rlen);

  return me->md.blocked_io_bytes;
}

PRInt32 _PR_MD_SENDFILE(PRFileDesc* sock, PRSendFileData* sfd, PRInt32 flags,
                        PRIntervalTime timeout) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRInt32 tflags;
  int rv, err;

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return -1;
  }

  if (!sock->secret->md.io_model_committed) {
    rv = _md_Associate((HANDLE)sock->secret->md.osfd);
    PR_ASSERT(0 != rv);
    sock->secret->md.io_model_committed = PR_TRUE;
  }
  if (!me->md.xmit_bufs) {
    me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
    if (!me->md.xmit_bufs) {
      PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
      return -1;
    }
  }
  me->md.xmit_bufs->Head = (void*)sfd->header;
  me->md.xmit_bufs->HeadLength = sfd->hlen;
  me->md.xmit_bufs->Tail = (void*)sfd->trailer;
  me->md.xmit_bufs->TailLength = sfd->tlen;

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
  me->md.overlapped.overlapped.Offset = sfd->file_offset;
  if (_native_threads_only) {
    me->md.overlapped.overlapped.hEvent = me->md.thr_event;
  }

  tflags = 0;
  if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
    tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
  }

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);
  me->io_fd = sock->secret->md.osfd;

  rv = TransmitFile((SOCKET)sock->secret->md.osfd,
                    (HANDLE)sfd->fd->secret->md.osfd, (DWORD)sfd->file_nbytes,
                    (DWORD)0, (LPOVERLAPPED) & (me->md.overlapped.overlapped),
                    (TRANSMIT_FILE_BUFFERS*)me->md.xmit_bufs, (DWORD)tflags);
  if ((rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return -1;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_TRANSMITFILE_ERROR(err);
    return -1;
  }

  if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
    PR_ASSERT(0);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

  if (me->md.blocked_io_status == 0) {
    _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
    return -1;
  }

  if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
    _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af);
  }

  PR_ASSERT(me->io_pending == PR_FALSE);

  return me->md.blocked_io_bytes;
}

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

  if (_NT_USE_NB_IO(fd)) {
    if (!fd->secret->md.io_model_committed) {
      rv = _md_MakeNonblock((HANDLE)osfd);
      PR_ASSERT(0 != rv);
      fd->secret->md.io_model_committed = PR_TRUE;
    }
    return _nt_nonblock_recv(fd, buf, amount, flags, timeout);
  }

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return -1;
  }

  if (!fd->secret->md.io_model_committed) {
    rv = _md_Associate((HANDLE)osfd);
    PR_ASSERT(0 != rv);
    fd->secret->md.io_model_committed = PR_TRUE;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
  if (_native_threads_only) {
    me->md.overlapped.overlapped.hEvent = me->md.thr_event;
  }

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);
  me->io_fd = osfd;

  rv = ReadFile((HANDLE)osfd, buf, amount, &bytes,
                &(me->md.overlapped.overlapped));
  if ((rv == 0) && (GetLastError() != ERROR_IO_PENDING)) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return -1;
    }
    _PR_THREAD_UNLOCK(me);

    if ((err = GetLastError()) == ERROR_HANDLE_EOF) {
      return 0;
    }
    _PR_MD_MAP_READ_ERROR(err);
    return -1;
  }

  if (_native_threads_only && rv) {
    _native_thread_io_nowait(me, rv, bytes);
  } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
    PR_ASSERT(0);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

  if (me->md.blocked_io_status == 0) {
    if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
      return 0;
    }
    _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE);

  return me->md.blocked_io_bytes;
}

PRInt32 _PR_MD_SEND(PRFileDesc* fd, const void* buf, PRInt32 amount,
                    PRIntn flags, PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRThread* me = _PR_MD_CURRENT_THREAD();
  int bytes;
  int rv, err;

  if (_NT_USE_NB_IO(fd)) {
    if (!fd->secret->md.io_model_committed) {
      rv = _md_MakeNonblock((HANDLE)osfd);
      PR_ASSERT(0 != rv);
      fd->secret->md.io_model_committed = PR_TRUE;
    }
    return _nt_nonblock_send(fd, (char*)buf, amount, timeout);
  }

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return -1;
  }

  if (!fd->secret->md.io_model_committed) {
    rv = _md_Associate((HANDLE)osfd);
    PR_ASSERT(0 != rv);
    fd->secret->md.io_model_committed = PR_TRUE;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
  if (_native_threads_only) {
    me->md.overlapped.overlapped.hEvent = me->md.thr_event;
  }

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);
  me->io_fd = osfd;

  rv = WriteFile((HANDLE)osfd, buf, amount, &bytes,
                 &(me->md.overlapped.overlapped));
  if ((rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return -1;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_WRITE_ERROR(err);
    return -1;
  }

  if (_native_threads_only && rv) {
    _native_thread_io_nowait(me, rv, bytes);
  } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
    PR_ASSERT(0);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

  if (me->md.blocked_io_status == 0) {
    _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
    return -1;
  }

  PR_ASSERT(me->io_pending == PR_FALSE);

  return me->md.blocked_io_bytes;
}

PRInt32 _PR_MD_SENDTO(PRFileDesc* fd, const void* buf, PRInt32 amount,
                      PRIntn flags, const PRNetAddr* addr, PRUint32 addrlen,
                      PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv;

  if (!fd->secret->md.io_model_committed) {
    rv = _md_MakeNonblock((HANDLE)osfd);
    PR_ASSERT(0 != rv);
    fd->secret->md.io_model_committed = PR_TRUE;
  }
  if (_NT_USE_NB_IO(fd)) {
    return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr*)addr, addrlen,
                               timeout);
  } else {
    return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
  }
}

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

  if (!fd->secret->md.io_model_committed) {
    rv = _md_MakeNonblock((HANDLE)osfd);
    PR_ASSERT(0 != rv);
    fd->secret->md.io_model_committed = PR_TRUE;
  }
  if (_NT_USE_NB_IO(fd)) {
    return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr*)addr,
                                 addrlen, timeout);
  } else {
    return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
  }
}

/* XXXMB - for now this is a sockets call only */
PRInt32 _PR_MD_WRITEV(PRFileDesc* fd, const PRIOVec* iov, PRInt32 iov_size,
                      PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  int index;
  int sent = 0;
  int rv;

  if (_NT_USE_NB_IO(fd)) {
    if (!fd->secret->md.io_model_committed) {
      rv = _md_MakeNonblock((HANDLE)osfd);
      PR_ASSERT(0 != rv);
      fd->secret->md.io_model_committed = PR_TRUE;
    }
    return _nt_nonblock_writev(fd, iov, iov_size, timeout);
  }

  for (index = 0; index < iov_size; index++) {
    rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
    if (rv > 0) {
      sent += rv;
    }
    if (rv != iov[index].iov_len) {
      if (sent <= 0) {
        return -1;
      }
      return -1;
    }
  }

  return sent;
}

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

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

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

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

PRStatus _PR_MD_GETSOCKNAME(PRFileDesc* fd, PRNetAddr* addr, PRUint32* len) {
  PRInt32 rv;

  rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr*)addr, len);
  if (rv == 0) {
    return PR_SUCCESS;
  } else {
    _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
    return PR_FAILURE;
  }
}

PRStatus _PR_MD_GETPEERNAME(PRFileDesc* fd, PRNetAddr* addr, PRUint32* len) {
  PRInt32 rv;

  /*
   * NT has a bug that, when invoked on a socket accepted by
   * AcceptEx(), getpeername() returns an all-zero peer address.
   * To work around this bug, we store the peer's address (returned
   * by AcceptEx()) with the socket fd and use the cached peer
   * address if the socket is an accepted socket.
   */

  if (fd->secret->md.accepted_socket) {
    INT seconds;
    INT bytes = sizeof(seconds);

    /*
     * Determine if the socket is connected.
     */

    rv = getsockopt(fd->secret->md.osfd, SOL_SOCKET, SO_CONNECT_TIME,
                    (char*)&seconds, (PINT)&bytes);
    if (rv == NO_ERROR) {
      if (seconds == 0xffffffff) {
        PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
        return PR_FAILURE;
      }
      *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr);
      memcpy(addr, &fd->secret->md.peer_addr, *len);
      return PR_SUCCESS;
    } else {
      _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
      return PR_FAILURE;
    }
  } else {
    rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr*)addr, len);
    if (rv == 0) {
      return PR_SUCCESS;
    } else {
      _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
      return PR_FAILURE;
    }
  }
}

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

  rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
  if (rv == 0) {
    return PR_SUCCESS;
  } else {
    _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
    return PR_FAILURE;
  }
}

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

  rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
  if (rv == 0) {
    return PR_SUCCESS;
  } else {
    _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
    return PR_FAILURE;
  }
}

/* --- FILE IO ----------------------------------------------------------- */

PROsfd _PR_MD_OPEN(const char* name, PRIntn osflags, PRIntn mode) {
  HANDLE file;
  PRInt32 access = 0;
  PRInt32 flags = 0;
  PRInt32 flag6 = 0;

  if (osflags & PR_SYNC) {
    flag6 = FILE_FLAG_WRITE_THROUGH;
  }

  if (osflags & PR_RDONLY || osflags & PR_RDWR) {
    access |= GENERIC_READ;
  }
  if (osflags & PR_WRONLY || osflags & PR_RDWR) {
    access |= GENERIC_WRITE;
  }

  if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) {
    flags = CREATE_NEW;
  } else if (osflags & PR_CREATE_FILE) {
    flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
  } else if (osflags & PR_TRUNCATE) {
    flags = TRUNCATE_EXISTING;
  } else {
    flags = OPEN_EXISTING;
  }

  flag6 |= FILE_FLAG_OVERLAPPED;

  file = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                    flags, flag6, NULL);
  if (file == INVALID_HANDLE_VALUE) {
    _PR_MD_MAP_OPEN_ERROR(GetLastError());
    return -1;
  }

  if (osflags & PR_APPEND) {
    if (SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF) {
      _PR_MD_MAP_LSEEK_ERROR(GetLastError());
      CloseHandle(file);
      return -1;
    }
  }

  return (PROsfd)file;
}

PROsfd _PR_MD_OPEN_FILE(const char* name, PRIntn osflags, PRIntn mode) {
  HANDLE file;
  PRInt32 access = 0;
  PRInt32 flags = 0;
  PRInt32 flag6 = 0;
  SECURITY_ATTRIBUTES sa;
  LPSECURITY_ATTRIBUTES lpSA = NULL;
  PSECURITY_DESCRIPTOR pSD = NULL;
  PACL pACL = NULL;

  if (osflags & PR_SYNC) {
    flag6 = FILE_FLAG_WRITE_THROUGH;
  }

  if (osflags & PR_RDONLY || osflags & PR_RDWR) {
    access |= GENERIC_READ;
  }
  if (osflags & PR_WRONLY || osflags & PR_RDWR) {
    access |= GENERIC_WRITE;
  }

  if (osflags & PR_CREATE_FILE && osflags & PR_EXCL) {
    flags = CREATE_NEW;
  } else if (osflags & PR_CREATE_FILE) {
    flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
  } else if (osflags & PR_TRUNCATE) {
    flags = TRUNCATE_EXISTING;
  } else {
    flags = OPEN_EXISTING;
  }

  flag6 |= FILE_FLAG_OVERLAPPED;

  if (osflags & PR_CREATE_FILE) {
    if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable, &pSD, &pACL) ==
        PR_SUCCESS) {
      sa.nLength = sizeof(sa);
      sa.lpSecurityDescriptor = pSD;
      sa.bInheritHandle = FALSE;
      lpSA = &sa;
    }
  }
  file = CreateFile(name, access, FILE_SHARE_READ | FILE_SHARE_WRITE, lpSA,
                    flags, flag6, NULL);
  if (lpSA != NULL) {
    _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
  }
  if (file == INVALID_HANDLE_VALUE) {
    _PR_MD_MAP_OPEN_ERROR(GetLastError());
    return -1;
  }

  if (osflags & PR_APPEND) {
    if (SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF) {
      _PR_MD_MAP_LSEEK_ERROR(GetLastError());
      CloseHandle(file);
      return -1;
    }
  }

  return (PROsfd)file;
}

PRInt32 _PR_MD_READ(PRFileDesc* fd, void* buf, PRInt32 len) {
  PROsfd f = fd->secret->md.osfd;
  PRUint32 bytes;
  int rv, err;
  LONG hiOffset = 0;
  LONG loOffset;
  LARGE_INTEGER offset; /* use for a normalized add of len to offset */

  if (!fd->secret->md.sync_file_io) {
    PRThread* me = _PR_MD_CURRENT_THREAD();

    if (me->io_suspended) {
      PR_SetError(PR_INVALID_STATE_ERROR, 0);
      return -1;
    }

    memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));

    me->md.overlapped.overlapped.Offset = SetFilePointer(
        (HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
    PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) ||
              (GetLastError() == NO_ERROR));

    if (fd->secret->inheritable == _PR_TRI_TRUE) {
      rv = ReadFile((HANDLE)f, (LPVOID)buf, len, &bytes,
                    &me->md.overlapped.overlapped);
      if (rv != 0) {
        loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
        PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
        return bytes;
      }
      err = GetLastError();
      if (err == ERROR_IO_PENDING) {
        rv = GetOverlappedResult((HANDLE)f, &me->md.overlapped.overlapped,
                                 &bytes, TRUE);
        if (rv != 0) {
          loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
          PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
          return bytes;
        }
        err = GetLastError();
      }
      if (err == ERROR_HANDLE_EOF) {
        return 0;
      } else {
        _PR_MD_MAP_READ_ERROR(err);
        return -1;
      }
    } else {
      if (!fd->secret->md.io_model_committed) {
        rv = _md_Associate((HANDLE)f);
        PR_ASSERT(rv != 0);
        fd->secret->md.io_model_committed = PR_TRUE;
      }

      if (_native_threads_only) {
        me->md.overlapped.overlapped.hEvent = me->md.thr_event;
      }

      _PR_THREAD_LOCK(me);
      if (_PR_PENDING_INTERRUPT(me)) {
        me->flags &= ~_PR_INTERRUPT;
        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
        _PR_THREAD_UNLOCK(me);
        return -1;
      }
      me->io_pending = PR_TRUE;
      me->state = _PR_IO_WAIT;
      _PR_THREAD_UNLOCK(me);
      me->io_fd = f;

      rv = ReadFile((HANDLE)f, (LPVOID)buf, len, &bytes,
                    &me->md.overlapped.overlapped);
      if ((rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
        _PR_THREAD_LOCK(me);
        me->io_pending = PR_FALSE;
        me->state = _PR_RUNNING;
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          _PR_THREAD_UNLOCK(me);
          return -1;
        }
        _PR_THREAD_UNLOCK(me);

        if (err == ERROR_HANDLE_EOF) {
          return 0;
        }
        _PR_MD_MAP_READ_ERROR(err);
        return -1;
      }

      if (_native_threads_only && rv) {
        _native_thread_io_nowait(me, rv, bytes);
      } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
        PR_ASSERT(0);
        return -1;
      }

      PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

      if (me->md.blocked_io_status == 0) {
        if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
          return 0;
        }
        _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
        return -1;
      }

      /* Apply the workaround from bug 70765 (see _PR_MD_WRITE)
       * to the reading code, too. */

      offset.LowPart = me->md.overlapped.overlapped.Offset;
      offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
      offset.QuadPart += me->md.blocked_io_bytes;

      SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);

      PR_ASSERT(me->io_pending == PR_FALSE);

      return me->md.blocked_io_bytes;
    }
  } else {
    rv = ReadFile((HANDLE)f, (LPVOID)buf, len, &bytes, NULL);
    if (rv == 0) {
      err = GetLastError();
      /* ERROR_HANDLE_EOF can only be returned by async io */
      PR_ASSERT(err != ERROR_HANDLE_EOF);
      if (err == ERROR_BROKEN_PIPE) {
        /* The write end of the pipe has been closed. */
        return 0;
      }
      _PR_MD_MAP_READ_ERROR(err);
      return -1;
    }
    return bytes;
  }
}

PRInt32 _PR_MD_WRITE(PRFileDesc* fd, const void* buf, PRInt32 len) {
  PROsfd f = fd->secret->md.osfd;
  PRInt32 bytes;
  int rv, err;
  LONG hiOffset = 0;
  LONG loOffset;
  LARGE_INTEGER offset; /* use for the calculation of the new offset */

  if (!fd->secret->md.sync_file_io) {
    PRThread* me = _PR_MD_CURRENT_THREAD();

    if (me->io_suspended) {
      PR_SetError(PR_INVALID_STATE_ERROR, 0);
      return -1;
    }

    memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));

    me->md.overlapped.overlapped.Offset = SetFilePointer(
        (HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
    PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) ||
              (GetLastError() == NO_ERROR));

    if (fd->secret->inheritable == _PR_TRI_TRUE) {
      rv = WriteFile((HANDLE)f, (LPVOID)buf, len, &bytes,
                     &me->md.overlapped.overlapped);
      if (rv != 0) {
        loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
        PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
        return bytes;
      }
      err = GetLastError();
      if (err == ERROR_IO_PENDING) {
        rv = GetOverlappedResult((HANDLE)f, &me->md.overlapped.overlapped,
                                 &bytes, TRUE);
        if (rv != 0) {
          loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
          PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
          return bytes;
        }
        err = GetLastError();
      }
      _PR_MD_MAP_READ_ERROR(err);
      return -1;
    } else {
      if (!fd->secret->md.io_model_committed) {
        rv = _md_Associate((HANDLE)f);
        PR_ASSERT(rv != 0);
        fd->secret->md.io_model_committed = PR_TRUE;
      }
      if (_native_threads_only) {
        me->md.overlapped.overlapped.hEvent = me->md.thr_event;
      }

      _PR_THREAD_LOCK(me);
      if (_PR_PENDING_INTERRUPT(me)) {
        me->flags &= ~_PR_INTERRUPT;
        PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
        _PR_THREAD_UNLOCK(me);
        return -1;
      }
      me->io_pending = PR_TRUE;
      me->state = _PR_IO_WAIT;
      _PR_THREAD_UNLOCK(me);
      me->io_fd = f;

      rv = WriteFile((HANDLE)f, buf, len, &bytes,
                     &(me->md.overlapped.overlapped));
      if ((rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
        _PR_THREAD_LOCK(me);
        me->io_pending = PR_FALSE;
        me->state = _PR_RUNNING;
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          _PR_THREAD_UNLOCK(me);
          return -1;
        }
        _PR_THREAD_UNLOCK(me);

        _PR_MD_MAP_WRITE_ERROR(err);
        return -1;
      }

      if (_native_threads_only && rv) {
        _native_thread_io_nowait(me, rv, bytes);
      } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
        PR_ASSERT(0);
        return -1;
      }

      PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);

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

      if (me->md.blocked_io_status == 0) {
        _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
        return -1;
      }

      /*
       * Moving the file pointer by a relative offset (FILE_CURRENT)
       * does not work with a file on a network drive exported by a
       * Win2K system.  We still don't know why.  A workaround is to
       * move the file pointer by an absolute offset (FILE_BEGIN).
       * (Bugzilla bug 70765)
       */
      offset.LowPart = me->md.overlapped.overlapped.Offset;
      offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
      offset.QuadPart += me->md.blocked_io_bytes;

      SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);

      PR_ASSERT(me->io_pending == PR_FALSE);

      return me->md.blocked_io_bytes;
    }
  } else {
    rv = WriteFile((HANDLE)f, buf, len, &bytes, NULL);
    if (rv == 0) {
      _PR_MD_MAP_WRITE_ERROR(GetLastError());
      return -1;
    }
    return bytes;
  }
}

PRInt32 _PR_MD_SOCKETAVAILABLE(PRFileDesc* fd) {
  PRInt32 result;

  if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
    return -1;
  }
  return result;
}

PRInt32 _PR_MD_PIPEAVAILABLE(PRFileDesc* fd) {
  if (NULL == fd) {
    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
  } else {
    PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
  }
  return -1;
}

PROffset32 _PR_MD_LSEEK(PRFileDesc* fd, PROffset32 offset,
                        PRSeekWhence whence) {
  DWORD moveMethod;
  PROffset32 rv;

  switch (whence) {
    case PR_SEEK_SET:
      moveMethod = FILE_BEGIN;
      break;
    case PR_SEEK_CUR:
      moveMethod = FILE_CURRENT;
      break;
    case PR_SEEK_END:
      moveMethod = FILE_END;
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      return -1;
  }

  rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);

  /*
   * If the lpDistanceToMoveHigh argument (third argument) is
   * NULL, SetFilePointer returns 0xffffffff on failure.
   */
  if (-1 == rv) {
    _PR_MD_MAP_LSEEK_ERROR(GetLastError());
  }
  return rv;
}

PROffset64 _PR_MD_LSEEK64(PRFileDesc* fd, PROffset64 offset,
                          PRSeekWhence whence) {
  DWORD moveMethod;
  LARGE_INTEGER li;
  DWORD err;

  switch (whence) {
    case PR_SEEK_SET:
      moveMethod = FILE_BEGIN;
      break;
    case PR_SEEK_CUR:
      moveMethod = FILE_CURRENT;
      break;
    case PR_SEEK_END:
      moveMethod = FILE_END;
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      return -1;
  }

  li.QuadPart = offset;
  li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd, li.LowPart,
                              &li.HighPart, moveMethod);

  if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
    _PR_MD_MAP_LSEEK_ERROR(err);
    li.QuadPart = -1;
  }
  return li.QuadPart;
}

/*
 * This is documented to succeed on read-only files, but Win32's
 * FlushFileBuffers functions fails with "access denied" in such a
 * case.  So we only signal an error if the error is *not* "access
 * denied".
 */
PRInt32 _PR_MD_FSYNC(PRFileDesc* fd) {
  /*
   * From the documentation:
   *
   *     On Windows NT, the function FlushFileBuffers fails if hFile
   *     is a handle to console output. That is because console
   *     output is not buffered. The function returns FALSE, and
   *     GetLastError returns ERROR_INVALID_HANDLE.
   *
   * On the other hand, on Win95, it returns without error.  I cannot
   * assume that 0, 1, and 2 are console, because if someone closes
   * System.out and then opens a file, they might get file descriptor
   * 1.  An error on *that* version of 1 should be reported, whereas
   * an error on System.out (which was the original 1) should be
   * ignored.  So I use isatty() to ensure that such an error was
   * because of this, and if it was, I ignore the error.
   */

  BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);

  if (!ok) {
    DWORD err = GetLastError();

    if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */
      _PR_MD_MAP_FSYNC_ERROR(err);
      return -1;
    }
  }
  return 0;
}

PRInt32 _PR_MD_CLOSE(PROsfd osfd, PRBool socket) {
  PRInt32 rv;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (socket) {
    rv = closesocket((SOCKET)osfd);
    if (rv < 0) {
      _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
    }
  } else {
    rv = CloseHandle((HANDLE)osfd) ? 0 : -1;
    if (rv < 0) {
      _PR_MD_MAP_CLOSE_ERROR(GetLastError());
    }
  }

  if (rv == 0 && me->io_suspended) {
    if (me->io_fd == osfd) {
      PRBool fWait;

      _PR_THREAD_LOCK(me);
      me->state = _PR_IO_WAIT;
      /* The IO could have completed on another thread just after
       * calling closesocket while the io_suspended flag was true.
       * So we now grab the lock to do a safe check on io_pending to
       * see if we need to wait or not.
       */
      fWait = me->io_pending;
      me->io_suspended = PR_FALSE;
      me->md.interrupt_disabled = PR_TRUE;
      _PR_THREAD_UNLOCK(me);

      if (fWait) {
        _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
      }
      PR_ASSERT(me->io_suspended == PR_FALSE);
      PR_ASSERT(me->io_pending == PR_FALSE);
      /*
       * I/O operation is no longer pending; the thread can now
       * run on any cpu
       */
      _PR_THREAD_LOCK(me);
      me->md.interrupt_disabled = PR_FALSE;
      me->md.thr_bound_cpu = NULL;
      me->io_suspended = PR_FALSE;
      me->io_pending = PR_FALSE;
      me->state = _PR_RUNNING;
      _PR_THREAD_UNLOCK(me);
    }
  }
  return rv;
}

PRStatus _PR_MD_SET_FD_INHERITABLE(PRFileDesc* fd, PRBool inheritable) {
  BOOL rv;

  if (fd->secret->md.io_model_committed) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    return PR_FAILURE;
  }
  rv = SetHandleInformation((HANDLE)fd->secret->md.osfd, HANDLE_FLAG_INHERIT,
                            inheritable ? HANDLE_FLAG_INHERIT : 0);
  if (0 == rv) {
    _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
    return PR_FAILURE;
  }
  return PR_SUCCESS;
}

void _PR_MD_INIT_FD_INHERITABLE(PRFileDesc* fd, PRBool imported) {
  if (imported) {
    fd->secret->inheritable = _PR_TRI_UNKNOWN;
  } else {
    fd->secret->inheritable = _PR_TRI_FALSE;
  }
}

void _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc* fd) {
  DWORD flags;

  PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
  if (fd->secret->md.io_model_committed) {
    return;
  }
  if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
    if (flags & HANDLE_FLAG_INHERIT) {
      fd->secret->inheritable = _PR_TRI_TRUE;
    } else {
      fd->secret->inheritable = _PR_TRI_FALSE;
    }
  }
}

/* --- DIR IO ------------------------------------------------------------ */
#define GetFileFromDIR(d) (d)->d_entry.cFileName
#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)

void FlipSlashes(char* cp, int len) {
  while (--len >= 0) {
    if (cp[0] == '/') {
      cp[0] = PR_DIRECTORY_SEPARATOR;
    }
    cp = _mbsinc(cp);
  }
} /* end FlipSlashes() */

/*
**
** Local implementations of standard Unix RTL functions which are not provided
** by the VC RTL.
**
*/

PRInt32 _PR_MD_CLOSE_DIR(_MDDir* d) {
  if (d) {
    if (FindClose(d->d_hdl)) {
      d->magic = (PRUint32)-1;
      return 0;
    } else {
      _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
      return -1;
    }
  }
  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  return -1;
}

PRStatus _PR_MD_OPEN_DIR(_MDDir* d, const char* name) {
  char filename[MAX_PATH];
  int len;

  len = strlen(name);
  /* Need 5 bytes for \*.* and the trailing null byte. */
  if (len + 5 > MAX_PATH) {
    PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
    return PR_FAILURE;
  }
  strcpy(filename, name);

  /*
   * If 'name' ends in a slash or backslash, do not append
   * another backslash.
   */
  if (IsPrevCharSlash(filename, filename + len)) {
    len--;
  }
  strcpy(&filename[len], "\\*.*");
  FlipSlashes(filename, strlen(filename));

  d->d_hdl = FindFirstFile(filename, &(d->d_entry));
  if (d->d_hdl == INVALID_HANDLE_VALUE) {
    _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
    return PR_FAILURE;
  }
  d->firstEntry = PR_TRUE;
  d->magic = _MD_MAGIC_DIR;
  return PR_SUCCESS;
}

char* _PR_MD_READ_DIR(_MDDir* d, PRIntn flags) {
  PRInt32 err;
  BOOL rv;
  char* fileName;

  if (d) {
    while (1) {
      if (d->firstEntry) {
        d->firstEntry = PR_FALSE;
        rv = 1;
      } else {
        rv = FindNextFile(d->d_hdl, &(d->d_entry));
      }
      if (rv == 0) {
        break;
      }
      fileName = GetFileFromDIR(d);
      if ((flags & PR_SKIP_DOT) && (fileName[0] == '.') &&
          (fileName[1] == '\0')) {
        continue;
      }
      if ((flags & PR_SKIP_DOT_DOT) && (fileName[0] == '.') &&
          (fileName[1] == '.') && (fileName[2] == '\0')) {
        continue;
      }
      if ((flags & PR_SKIP_HIDDEN) && FileIsHidden(d)) {
        continue;
      }
      return fileName;
    }
    err = GetLastError();
    PR_ASSERT(NO_ERROR != err);
    _PR_MD_MAP_READDIR_ERROR(err);
    return NULL;
  }
  PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  return NULL;
}

PRInt32 _PR_MD_DELETE(const char* name) {
  if (DeleteFile(name)) {
    return 0;
  } else {
    _PR_MD_MAP_DELETE_ERROR(GetLastError());
    return -1;
  }
}

void _PR_FileTimeToPRTime(const FILETIME* filetime, PRTime* prtm) {
  PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
  CopyMemory(prtm, filetime, sizeof(PRTime));
#ifdef __GNUC__
  *prtm = (*prtm - _pr_filetime_offset) / 10LL;
#else
  *prtm = (*prtm - _pr_filetime_offset) / 10i64;
#endif

#ifdef DEBUG
  /* Doublecheck our calculation. */
  {
    SYSTEMTIME systime;
    PRExplodedTime etm;
    PRTime cmp; /* for comparison */
    BOOL rv;

    rv = FileTimeToSystemTime(filetime, &systime);
    PR_ASSERT(0 != rv);

    /*
     * PR_ImplodeTime ignores wday and yday.
     */
    etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
    etm.tm_sec = systime.wSecond;
    etm.tm_min = systime.wMinute;
    etm.tm_hour = systime.wHour;
    etm.tm_mday = systime.wDay;
    etm.tm_month = systime.wMonth - 1;
    etm.tm_year = systime.wYear;
    /*
     * It is not well-documented what time zone the FILETIME's
     * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
     * But BY_HANDLE_FILE_INFORMATION is unclear about this.
     * By our best judgement, we assume that FILETIME is in UTC.
     */
    etm.tm_params.tp_gmt_offset = 0;
    etm.tm_params.tp_dst_offset = 0;
    cmp = PR_ImplodeTime(&etm);

    /*
     * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
     * microseconds to milliseconds before doing the comparison.
     */
    PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
  }
#endif /* DEBUG */
}

PRInt32 _PR_MD_STAT(const char* fn, struct stat* info) {
  PRInt32 rv;

  rv = _stat(fn, (struct _stat*)info);
  if (-1 == rv) {
    /*
     * Check for MSVC runtime library _stat() bug.
     * (It's really a bug in FindFirstFile().)
     * If a pathname ends in a backslash or slash,
     * e.g., c:\temp\ or c:/temp/, _stat() will fail.
     * Note: a pathname ending in a slash (e.g., c:/temp/)
     * can be handled by _stat() on NT but not on Win95.
     *
     * We remove the backslash or slash at the end and
     * try again.
     */

    int len = strlen(fn);
    if (len > 0 && len <= _MAX_PATH && IsPrevCharSlash(fn, fn + len)) {
      char newfn[_MAX_PATH + 1];

      strcpy(newfn, fn);
      newfn[len - 1] = '\0';
      rv = _stat(newfn, (struct _stat*)info);
    }
  }

  if (-1 == rv) {
    _PR_MD_MAP_STAT_ERROR(errno);
  }
  return rv;
}

#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')

static PRBool IsPrevCharSlash(const char* str, const char* current) {
  const char* prev;

  if (str >= current) {
    return PR_FALSE;
  }
  prev = _mbsdec(str, current);
  return (prev == current - 1) && _PR_IS_SLASH(*prev);
}

/*
 * IsRootDirectory --
 *
 * Return PR_TRUE if the pathname 'fn' is a valid root directory,
 * else return PR_FALSE.  The char buffer pointed to by 'fn' must
 * be writable.  During the execution of this function, the contents
 * of the buffer pointed to by 'fn' may be modified, but on return
 * the original contents will be restored.  'buflen' is the size of
 * the buffer pointed to by 'fn'.
 *
 * Root directories come in three formats:
 * 1. / or \, meaning the root directory of the current drive.
 * 2. C:/ or C:\, where C is a drive letter.
 * 3. \\<server name>\<share point name>\ or
 *    \\<server name>\<share point name>, meaning the root directory
 *    of a UNC (Universal Naming Convention) name.
 */

static PRBool IsRootDirectory(char* fn, size_t buflen) {
  char* p;
  PRBool slashAdded = PR_FALSE;
  PRBool rv = PR_FALSE;

  if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
    return PR_TRUE;
  }

  if (isalpha((unsigned char)fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2]) && fn[3] == '\0') {
    rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
    return rv;
  }

  /* The UNC root directory */

  if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
    /* The 'server' part should have at least one character. */
    p = &fn[2];
    if (*p == '\0' || _PR_IS_SLASH(*p)) {
      return PR_FALSE;
    }

    /* look for the next slash */
    do {
      p = _mbsinc(p);
    } while (*p != '\0' && !_PR_IS_SLASH(*p));
    if (*p == '\0') {
      return PR_FALSE;
    }

    /* The 'share' part should have at least one character. */
    p++;
    if (*p == '\0' || _PR_IS_SLASH(*p)) {
      return PR_FALSE;
    }

    /* look for the final slash */
    do {
      p = _mbsinc(p);
    } while (*p != '\0' && !_PR_IS_SLASH(*p));
    if (_PR_IS_SLASH(*p) && p[1] != '\0') {
      return PR_FALSE;
    }
    if (*p == '\0') {
      /*
       * GetDriveType() doesn't work correctly if the
       * path is of the form \\server\share, so we add
       * a final slash temporarily.
       */
      if ((p + 1) < (fn + buflen)) {
        *p++ = '\\';
        *p = '\0';
        slashAdded = PR_TRUE;
      } else {
        return PR_FALSE; /* name too long */
      }
    }
    rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
    /* restore the 'fn' buffer */
    if (slashAdded) {
      *--p = '\0';
    }
  }
  return rv;
}

PRInt32 _PR_MD_GETFILEINFO64(const char* fn, PRFileInfo64* info) {
  WIN32_FILE_ATTRIBUTE_DATA findFileData;

  if (NULL == fn || '\0' == *fn) {
    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    return -1;
  }

  if (!GetFileAttributesEx(fn, GetFileExInfoStandard, &findFileData)) {
    _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
    return -1;
  }

  if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    info->type = PR_FILE_DIRECTORY;
  } else {
    info->type = PR_FILE_FILE;
  }

  info->size = findFileData.nFileSizeHigh;
  info->size = (info->size << 32) + findFileData.nFileSizeLow;

  _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);

  if (0 == findFileData.ftCreationTime.dwLowDateTime &&
      0 == findFileData.ftCreationTime.dwHighDateTime) {
    info->creationTime = info->modifyTime;
  } else {
    _PR_FileTimeToPRTime(&findFileData.ftCreationTime, &info->creationTime);
  }

  return 0;
}

PRInt32 _PR_MD_GETFILEINFO(const char* fn, PRFileInfo* info) {
  PRFileInfo64 info64;
  PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
  if (0 == rv) {
    info->type = info64.type;
    info->size = (PRUint32)info64.size;
    info->modifyTime = info64.modifyTime;
    info->creationTime = info64.creationTime;
  }
  return rv;
}

PRInt32 _PR_MD_GETOPENFILEINFO64(const PRFileDesc* fd, PRFileInfo64* info) {
  int rv;

  BY_HANDLE_FILE_INFORMATION hinfo;

  rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
  if (rv == FALSE) {
    _PR_MD_MAP_FSTAT_ERROR(GetLastError());
    return -1;
  }

  if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    info->type = PR_FILE_DIRECTORY;
  } else {
    info->type = PR_FILE_FILE;
  }

  info->size = hinfo.nFileSizeHigh;
  info->size = (info->size << 32) + hinfo.nFileSizeLow;

  _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime));
  _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime));

  return 0;
}

PRInt32 _PR_MD_GETOPENFILEINFO(const PRFileDesc* fd, PRFileInfo* info) {
  int rv;

  BY_HANDLE_FILE_INFORMATION hinfo;

  rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
  if (rv == FALSE) {
    _PR_MD_MAP_FSTAT_ERROR(GetLastError());
    return -1;
  }

  if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
    info->type = PR_FILE_DIRECTORY;
  } else {
    info->type = PR_FILE_FILE;
  }

  info->size = hinfo.nFileSizeLow;

  _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime));
  _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime));

  return 0;
}

PRInt32 _PR_MD_RENAME(const char* from, const char* to) {
  /* Does this work with dot-relative pathnames? */
  if (MoveFile(from, to)) {
    return 0;
  } else {
    _PR_MD_MAP_RENAME_ERROR(GetLastError());
    return -1;
  }
}

PRInt32 _PR_MD_ACCESS(const char* name, PRAccessHow how) {
  PRInt32 rv;

  switch (how) {
    case PR_ACCESS_WRITE_OK:
      rv = _access(name, 02);
      break;
    case PR_ACCESS_READ_OK:
      rv = _access(name, 04);
      break;
    case PR_ACCESS_EXISTS:
      rv = _access(name, 00);
      break;
    default:
      PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
      return -1;
  }
  if (rv < 0) {
    _PR_MD_MAP_ACCESS_ERROR(errno);
  }
  return rv;
}

PRInt32 _PR_MD_MKDIR(const char* name, PRIntn mode) {
  /* XXXMB - how to translate the "mode"??? */
  if (CreateDirectory(name, NULL)) {
    return 0;
  } else {
    _PR_MD_MAP_MKDIR_ERROR(GetLastError());
    return -1;
  }
}

PRInt32 _PR_MD_MAKE_DIR(const char* name, PRIntn mode) {
  BOOL rv;
  SECURITY_ATTRIBUTES sa;
  LPSECURITY_ATTRIBUTES lpSA = NULL;
  PSECURITY_DESCRIPTOR pSD = NULL;
  PACL pACL = NULL;

  if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable, &pSD, &pACL) ==
      PR_SUCCESS) {
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = pSD;
    sa.bInheritHandle = FALSE;
    lpSA = &sa;
  }
  rv = CreateDirectory(name, lpSA);
  if (lpSA != NULL) {
    _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
  }
  if (rv) {
    return 0;
  } else {
    _PR_MD_MAP_MKDIR_ERROR(GetLastError());
    return -1;
  }
}

PRInt32 _PR_MD_RMDIR(const char* name) {
  if (RemoveDirectory(name)) {
    return 0;
  } else {
    _PR_MD_MAP_RMDIR_ERROR(GetLastError());
    return -1;
  }
}

PRStatus _PR_MD_LOCKFILE(PROsfd f) {
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return PR_FAILURE;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);

  rv = LockFileEx((HANDLE)f, LOCKFILE_EXCLUSIVE_LOCK, 0, 0x7fffffff, 0,
                  &me->md.overlapped.overlapped);

  if (_native_threads_only) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return PR_FAILURE;
    }
    _PR_THREAD_UNLOCK(me);

    if (rv == FALSE) {
      err = GetLastError();
      PR_ASSERT(err != ERROR_IO_PENDING);
      _PR_MD_MAP_LOCKF_ERROR(err);
      return PR_FAILURE;
    }
    return PR_SUCCESS;
  }

  /* HACK AROUND NT BUG
   * NT 3.51 has a bug.  In NT 3.51, if LockFileEx returns true, you
   * don't get any completion on the completion port.  This is a bug.
   *
   * They fixed it on NT4.0 so that you do get a completion.
   *
   * If we pretend we won't get a completion, NSPR gets confused later
   * when the unexpected completion arrives.  If we assume we do get
   * a completion, we hang on 3.51.  Worse, Microsoft informs me that the
   * behavior varies on 3.51 depending on if you are using a network
   * file system or a local disk!
   *
   * Solution:  For now, _nt_version_gets_lockfile_completion is set
   * depending on whether or not this system is EITHER
   *      - running NT 4.0
   *      - running NT 3.51 with a service pack greater than 5.
   *
   * In the meantime, this code may not work on network file systems.
   *
   */

  if (rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return PR_FAILURE;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_LOCKF_ERROR(err);
    return PR_FAILURE;
  }
#ifdef _NEED_351_FILE_LOCKING_HACK
  else if (rv) {
    /* If this is NT 3.51 and the file is local, then we won't get a
     * completion back from LockFile when it succeeded.
     */
    if (_nt_version_gets_lockfile_completion == PR_FALSE) {
      if (IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
        me->io_pending = PR_FALSE;
        me->state = _PR_RUNNING;
        return PR_SUCCESS;
      }
    }
  }
#endif /* _NEED_351_FILE_LOCKING_HACK */

  if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    _PR_THREAD_UNLOCK(me);
    return PR_FAILURE;
  }

  if (me->md.blocked_io_status == 0) {
    _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
    return PR_FAILURE;
  }

  return PR_SUCCESS;
}

PRStatus _PR_MD_TLOCKFILE(PROsfd f) {
  PRInt32 rv, err;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return PR_FAILURE;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));

  _PR_THREAD_LOCK(me);
  if (_PR_PENDING_INTERRUPT(me)) {
    me->flags &= ~_PR_INTERRUPT;
    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
    _PR_THREAD_UNLOCK(me);
    return -1;
  }
  me->io_pending = PR_TRUE;
  me->state = _PR_IO_WAIT;
  _PR_THREAD_UNLOCK(me);

  rv =
      LockFileEx((HANDLE)f, LOCKFILE_FAIL_IMMEDIATELY | LOCKFILE_EXCLUSIVE_LOCK,
                 0, 0x7fffffff, 0, &me->md.overlapped.overlapped);
  if (_native_threads_only) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return PR_FAILURE;
    }
    _PR_THREAD_UNLOCK(me);

    if (rv == FALSE) {
      err = GetLastError();
      PR_ASSERT(err != ERROR_IO_PENDING);
      _PR_MD_MAP_LOCKF_ERROR(err);
      return PR_FAILURE;
    }
    return PR_SUCCESS;
  }
  if (rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return PR_FAILURE;
    }
    _PR_THREAD_UNLOCK(me);

    _PR_MD_MAP_LOCKF_ERROR(err);
    return PR_FAILURE;
  }
#ifdef _NEED_351_FILE_LOCKING_HACK
  else if (rv) {
    /* If this is NT 3.51 and the file is local, then we won't get a
     * completion back from LockFile when it succeeded.
     */
    if (_nt_version_gets_lockfile_completion == PR_FALSE) {
      if (IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
        _PR_THREAD_LOCK(me);
        me->io_pending = PR_FALSE;
        me->state = _PR_RUNNING;
        if (_PR_PENDING_INTERRUPT(me)) {
          me->flags &= ~_PR_INTERRUPT;
          PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
          _PR_THREAD_UNLOCK(me);
          return PR_FAILURE;
        }
        _PR_THREAD_UNLOCK(me);

        return PR_SUCCESS;
      }
    }
  }
#endif /* _NEED_351_FILE_LOCKING_HACK */

  if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
    _PR_THREAD_LOCK(me);
    me->io_pending = PR_FALSE;
    me->state = _PR_RUNNING;
    if (_PR_PENDING_INTERRUPT(me)) {
      me->flags &= ~_PR_INTERRUPT;
      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
      _PR_THREAD_UNLOCK(me);
      return PR_FAILURE;
    }
    _PR_THREAD_UNLOCK(me);

    return PR_FAILURE;
  }

  if (me->md.blocked_io_status == 0) {
    _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
    return PR_FAILURE;
  }

  return PR_SUCCESS;
}

PRStatus _PR_MD_UNLOCKFILE(PROsfd f) {
  PRInt32 rv;
  PRThread* me = _PR_MD_CURRENT_THREAD();

  if (me->io_suspended) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return PR_FAILURE;
  }

  memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));

  rv = UnlockFileEx((HANDLE)f, 0, 0x7fffffff, 0, &me->md.overlapped.overlapped);

  if (rv) {
    return PR_SUCCESS;
  } else {
    int err = GetLastError();
    _PR_MD_MAP_LOCKF_ERROR(err);
    return PR_FAILURE;
  }
}

void _PR_MD_MAKE_NONBLOCK(PRFileDesc* f) {
  /*
   * On NT, we either call _md_Associate() or _md_MakeNonblock(),
   * depending on whether the socket is blocking or not.
   *
   * Once we associate a socket with the io completion port,
   * there is no way to disassociate it from the io completion
   * port.  So we have to call _md_Associate/_md_MakeNonblock
   * lazily.
   */
}

#ifdef _NEED_351_FILE_LOCKING_HACK
/***************
**
** Lockfile hacks
**
** The following code is a hack to work around a microsoft bug with lockfile.
** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
** get a completion back for files that are on local disks.  So, we need to
** know if a file is local or remote so we can tell if we should expect
** a completion.
**
** The only way to check if a file is local or remote based on the handle is
** to get the serial number for the volume it is mounted on and then to
** compare that with mounted drives.  This code caches the volume numbers of
** fixed disks and does a relatively quick check.
**
** Locking:  Since the only thing we ever do when multithreaded is a 32bit
**           assignment, we probably don't need locking.  It is included just
**           case anyway.
**
** Limitations:  Does not work on floppies because they are too slow
**               Unknown if it will work on wierdo 3rd party file systems
**
****************
*/

/* There can only be 26 drive letters on NT */
#  define _PR_MAX_DRIVES 26

_MDLock cachedVolumeLock;
DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
DWORD dwLastCachedDrive = 0;
DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */

PRBool IsFileLocalInit() {
  TCHAR lpBuffer[_PR_MAX_DRIVES * 5];
  DWORD nBufferLength = _PR_MAX_DRIVES * 5;
  DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
  DWORD dwIndex = 0;
  DWORD dwDriveType;
  DWORD dwVolumeSerialNumber;
  DWORD dwDriveIndex = 0;
  DWORD oldmode = (DWORD)-1;

  _MD_NEW_LOCK(&cachedVolumeLock);

  nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
  if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength) {
    return PR_FALSE;
  }

  // Calling GetVolumeInformation on a removeable drive where the
  // disk is currently removed will cause a dialog box to the
  // console.  This is not good.
  // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
  // damn dialog.

  dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
  oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);

  // now loop through the logical drives
  while (lpBuffer[dwIndex] != TEXT('\0')) {
    // skip the floppy drives.  This is *SLOW*
    if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
      /* Skip over floppies */;
    else {
      dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));

      dwDriveType = GetDriveType(&lpBuffer[dwIndex]);

      switch (dwDriveType) {
        // Ignore these drive types
        case 0:
        case 1:
        case DRIVE_REMOTE:
        default:  // If the drive type is unknown, ignore it.
          break;

        // Removable media drives can have different serial numbers
        // at different times, so cache the current serial number
        // but keep track of them so they can be rechecked if necessary.
        case DRIVE_REMOVABLE:

        // CDROM is a removable media
        case DRIVE_CDROM:

          // no idea if ramdisks can change serial numbers or not
          // but it doesn't hurt to treat them as removable.

        case DRIVE_RAMDISK:

          // Here is where we keep track of removable drives.
          dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;

          // removable drives fall through to fixed drives and get cached.

        case DRIVE_FIXED:

          // cache volume serial numbers.
          if (GetVolumeInformation(&lpBuffer[dwIndex], NULL, 0,
                                   &dwVolumeSerialNumber, NULL, NULL, NULL,
                                   0)) {
            if (dwLastCachedDrive < dwDriveIndex) {
              dwLastCachedDrive = dwDriveIndex;
            }
            dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
          }

          break;
      }
    }

    dwIndex += lstrlen(&lpBuffer[dwIndex]) + 1;
  }

  if (oldmode != (DWORD)-1) {
    SetErrorMode(oldmode);
    oldmode = (DWORD)-1;
  }

  return PR_TRUE;
}

PRInt32 IsFileLocal(HANDLE hFile) {
  DWORD dwIndex = 0, dwMask;
  BY_HANDLE_FILE_INFORMATION Info;
  TCHAR szDrive[4] = TEXT("C:\\");
  DWORD dwVolumeSerialNumber;
  DWORD oldmode = (DWORD)-1;
  int rv = _PR_REMOTE_FILE;

  if (!GetFileInformationByHandle(hFile, &Info)) {
    return -1;
  }

  // look to see if the volume serial number has been cached.
  _MD_LOCK(&cachedVolumeLock);
  while (dwIndex <= dwLastCachedDrive)
    if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber) {
      _MD_UNLOCK(&cachedVolumeLock);
      return _PR_LOCAL_FILE;
    }
  _MD_UNLOCK(&cachedVolumeLock);

  // volume serial number not found in the cache.  Check removable files.
  // removable drives are noted as a bitmask.  If the bit associated with
  // a specific drive is set, then we should query its volume serial number
  // as its possible it has changed.
  dwMask = dwRemoveableDrivesToCheck;
  dwIndex = 0;

  while (dwMask) {
    while (!(dwMask & 1)) {
      dwIndex++;
      dwMask = dwMask >> 1;
    }

    szDrive[0] = TEXT('A') + (TCHAR)dwIndex;

    // Calling GetVolumeInformation on a removeable drive where the
    // disk is currently removed will cause a dialog box to the
    // console.  This is not good.
    // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
    // dialog.

    oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);

    if (GetVolumeInformation(szDrive, NULL, 0, &dwVolumeSerialNumber, NULL,
                             NULL, NULL, 0)) {
      if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber) {
        _MD_LOCK(&cachedVolumeLock);
        if (dwLastCachedDrive < dwIndex) {
          dwLastCachedDrive = dwIndex;
        }
        dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
        _MD_UNLOCK(&cachedVolumeLock);
        rv = _PR_LOCAL_FILE;
      }
    }
    if (oldmode != (DWORD)-1) {
      SetErrorMode(oldmode);
      oldmode = (DWORD)-1;
    }

    if (rv == _PR_LOCAL_FILE) {
      return _PR_LOCAL_FILE;
    }

    dwIndex++;
    dwMask = dwMask >> 1;
  }

  return _PR_REMOTE_FILE;
}
#endif /* _NEED_351_FILE_LOCKING_HACK */

PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc* fd) {
  PRThread* me = _PR_MD_CURRENT_THREAD();
  PRBool fWait;
  PRFileDesc* bottom;

  bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
  if (!me->io_suspended || (NULL == bottom) ||
      (me->io_fd != bottom->secret->md.osfd)) {
    PR_SetError(PR_INVALID_STATE_ERROR, 0);
    return PR_FAILURE;
  }
  /*
   * The CancelIO operation has to be issued by the same NT thread that
   * issued the I/O operation
   */
  PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu));
  if (me->io_pending) {
    if (!CancelIo((HANDLE)bottom->secret->md.osfd)) {
      PR_SetError(PR_INVALID_STATE_ERROR, GetLastError());
      return PR_FAILURE;
    }
  }
  _PR_THREAD_LOCK(me);
  fWait = me->io_pending;
  me->io_suspended = PR_FALSE;
  me->state = _PR_IO_WAIT;
  me->md.interrupt_disabled = PR_TRUE;
  _PR_THREAD_UNLOCK(me);
  if (fWait) {
    _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
  }
  PR_ASSERT(me->io_suspended == PR_FALSE);
  PR_ASSERT(me->io_pending == PR_FALSE);

  _PR_THREAD_LOCK(me);
  me->md.interrupt_disabled = PR_FALSE;
  me->md.thr_bound_cpu = NULL;
  me->io_suspended = PR_FALSE;
  me->io_pending = PR_FALSE;
  me->state = _PR_RUNNING;
  _PR_THREAD_UNLOCK(me);
  return PR_SUCCESS;
}

static PROsfd _nt_nonblock_accept(PRFileDesc* fd, struct sockaddr* addr,
                                  int* addrlen, PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  SOCKET sock;
  PRInt32 rv, err;
  fd_set rd;
  struct timeval tv, *tvp;

  FD_ZERO(&rd);
  FD_SET((SOCKET)osfd, &rd);
  if (timeout == PR_INTERVAL_NO_TIMEOUT) {
    while ((sock = accept(osfd, addr, addrlen)) == -1) {
      if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
          (!fd->secret->nonblocking)) {
        if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, NULL)) == -1) {
          _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
          break;
        }
      } else {
        _PR_MD_MAP_ACCEPT_ERROR(err);
        break;
      }
    }
  } else if (timeout == PR_INTERVAL_NO_WAIT) {
    if ((sock = accept(osfd, addr, addrlen)) == -1) {
      if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
          (!fd->secret->nonblocking)) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
      } else {
        _PR_MD_MAP_ACCEPT_ERROR(err);
      }
    }
  } else {
  retry:
    if ((sock = accept(osfd, addr, addrlen)) == -1) {
      if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
          (!fd->secret->nonblocking)) {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;

        rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp);
        if (rv > 0) {
          goto retry;
        } else if (rv == 0) {
          PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        } else {
          _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        }
      } else {
        _PR_MD_MAP_ACCEPT_ERROR(err);
      }
    }
  }
  return (PROsfd)sock;
}

static PRInt32 _nt_nonblock_connect(PRFileDesc* fd, struct sockaddr* addr,
                                    int addrlen, PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv;
  int err;
  fd_set wr, ex;
  struct timeval tv, *tvp;
  int len;

  if ((rv = connect(osfd, addr, addrlen)) == -1) {
    if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
      if (timeout == PR_INTERVAL_NO_TIMEOUT) {
        tvp = NULL;
      } else {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;
      }
      FD_ZERO(&wr);
      FD_ZERO(&ex);
      FD_SET((SOCKET)osfd, &wr);
      FD_SET((SOCKET)osfd, &ex);
      if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wr, &ex, tvp)) == -1) {
        _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        return rv;
      }
      if (rv == 0) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        return -1;
      }
      /* Call Sleep(0) to work around a Winsock timeing bug. */
      Sleep(0);
      if (FD_ISSET((SOCKET)osfd, &ex)) {
        len = sizeof(err);
        if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char*)&err, &len) ==
            SOCKET_ERROR) {
          _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
          return -1;
        }
        _PR_MD_MAP_CONNECT_ERROR(err);
        return -1;
      }
      PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr));
      rv = 0;
    } else {
      _PR_MD_MAP_CONNECT_ERROR(err);
    }
  }
  return rv;
}

static PRInt32 _nt_nonblock_recv(PRFileDesc* fd, char* buf, int len, int flags,
                                 PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  struct timeval tv, *tvp;
  fd_set rd;
  int osflags;

  if (0 == flags) {
    osflags = 0;
  } else {
    PR_ASSERT(PR_MSG_PEEK == flags);
    osflags = MSG_PEEK;
  }
  while ((rv = recv(osfd, buf, len, osflags)) == -1) {
    if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
        (!fd->secret->nonblocking)) {
      FD_ZERO(&rd);
      FD_SET((SOCKET)osfd, &rd);
      if (timeout == PR_INTERVAL_NO_TIMEOUT) {
        tvp = NULL;
      } else {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;
      }
      if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp)) == -1) {
        _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        break;
      } else if (rv == 0) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        rv = -1;
        break;
      }
    } else {
      _PR_MD_MAP_RECV_ERROR(err);
      break;
    }
  }
  return (rv);
}

static PRInt32 _nt_nonblock_send(PRFileDesc* fd, char* buf, int len,
                                 PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  struct timeval tv, *tvp;
  fd_set wd;
  PRInt32 bytesSent = 0;

  while (bytesSent < len) {
    while ((rv = send(osfd, buf, len, 0)) == -1) {
      if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
          (!fd->secret->nonblocking)) {
        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
          tvp = NULL;
        } else {
          tv.tv_sec = PR_IntervalToSeconds(timeout);
          tv.tv_usec = PR_IntervalToMicroseconds(
              timeout - PR_SecondsToInterval(tv.tv_sec));
          tvp = &tv;
        }
        FD_ZERO(&wd);
        FD_SET((SOCKET)osfd, &wd);
        if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, tvp)) == -1) {
          _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
          return -1;
        }
        if (rv == 0) {
          PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
          return -1;
        }
      } else {
        _PR_MD_MAP_SEND_ERROR(err);
        return -1;
      }
    }
    bytesSent += rv;
    if (fd->secret->nonblocking) {
      break;
    }
    if (bytesSent < len) {
      if (timeout == PR_INTERVAL_NO_TIMEOUT) {
        tvp = NULL;
      } else {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;
      }
      FD_ZERO(&wd);
      FD_SET((SOCKET)osfd, &wd);
      if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, tvp)) == -1) {
        _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        return -1;
      }
      if (rv == 0) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        return -1;
      }
    }
  }
  return bytesSent;
}

static PRInt32 _nt_nonblock_writev(PRFileDesc* fd, const PRIOVec* iov, int size,
                                   PRIntervalTime timeout) {
  int index;
  int sent = 0;
  int rv;

  for (index = 0; index < size; index++) {
    rv =
        _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
    if (rv > 0) {
      sent += rv;
    }
    if (rv != iov[index].iov_len) {
      if (rv < 0) {
        if (fd->secret->nonblocking &&
            (PR_GetError() == PR_WOULD_BLOCK_ERROR) && (sent > 0)) {
          return sent;
        } else {
          return -1;
        }
      }
      /* Only a nonblocking socket can have partial sends */
      PR_ASSERT(fd->secret->nonblocking);
      return sent;
    }
  }

  return sent;
}

static PRInt32 _nt_nonblock_sendto(PRFileDesc* fd, const char* buf, int len,
                                   const struct sockaddr* addr, int addrlen,
                                   PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  struct timeval tv, *tvp;
  fd_set wd;
  PRInt32 bytesSent = 0;

  while (bytesSent < len) {
    while ((rv = sendto(osfd, buf, len, 0, addr, addrlen)) == -1) {
      if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
          (!fd->secret->nonblocking)) {
        if (timeout == PR_INTERVAL_NO_TIMEOUT) {
          tvp = NULL;
        } else {
          tv.tv_sec = PR_IntervalToSeconds(timeout);
          tv.tv_usec = PR_IntervalToMicroseconds(
              timeout - PR_SecondsToInterval(tv.tv_sec));
          tvp = &tv;
        }
        FD_ZERO(&wd);
        FD_SET((SOCKET)osfd, &wd);
        if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, tvp)) == -1) {
          _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
          return -1;
        }
        if (rv == 0) {
          PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
          return -1;
        }
      } else {
        _PR_MD_MAP_SENDTO_ERROR(err);
        return -1;
      }
    }
    bytesSent += rv;
    if (fd->secret->nonblocking) {
      break;
    }
    if (bytesSent < len) {
      if (timeout == PR_INTERVAL_NO_TIMEOUT) {
        tvp = NULL;
      } else {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;
      }
      FD_ZERO(&wd);
      FD_SET((SOCKET)osfd, &wd);
      if ((rv = _PR_NTFiberSafeSelect(0, NULL, &wd, NULL, tvp)) == -1) {
        _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        return -1;
      }
      if (rv == 0) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        return -1;
      }
    }
  }
  return bytesSent;
}

static PRInt32 _nt_nonblock_recvfrom(PRFileDesc* fd, char* buf, int len,
                                     struct sockaddr* addr, int* addrlen,
                                     PRIntervalTime timeout) {
  PROsfd osfd = fd->secret->md.osfd;
  PRInt32 rv, err;
  struct timeval tv, *tvp;
  fd_set rd;

  while ((rv = recvfrom(osfd, buf, len, 0, addr, addrlen)) == -1) {
    if (((err = WSAGetLastError()) == WSAEWOULDBLOCK) &&
        (!fd->secret->nonblocking)) {
      if (timeout == PR_INTERVAL_NO_TIMEOUT) {
        tvp = NULL;
      } else {
        tv.tv_sec = PR_IntervalToSeconds(timeout);
        tv.tv_usec = PR_IntervalToMicroseconds(timeout -
                                               PR_SecondsToInterval(tv.tv_sec));
        tvp = &tv;
      }
      FD_ZERO(&rd);
      FD_SET((SOCKET)osfd, &rd);
      if ((rv = _PR_NTFiberSafeSelect(0, &rd, NULL, NULL, tvp)) == -1) {
        _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
        break;
      } else if (rv == 0) {
        PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
        rv = -1;
        break;
      }
    } else {
      _PR_MD_MAP_RECVFROM_ERROR(err);
      break;
    }
  }
  return (rv);
}

/*
 * UDP support: the continuation thread functions and recvfrom and sendto.
 */

static void pt_InsertTimedInternal(pt_Continuation* op) {
  PRInt32 delta = 0;
  pt_Continuation* t_op = NULL;
  PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;

  /*
   * If this element operation isn't timed, it gets queued at the
   * end of the list (just after pt_tq.tail) and we're
   * finishd early.
   */
  if (PR_INTERVAL_NO_TIMEOUT == op->timeout) {
    t_op = pt_tq.tail; /* put it at the end */
    goto done;
  }

  /*
   * The rest of this routine actaully deals with timed ops.
   */

  if (NULL != pt_tq.op) {
    /*
     * To find where in the list to put the new operation, form
     * the absolute time the operations in question will expire.
     *
     * The new operation ('op') will expire at now() + op->timeout.
     *
     * The operation that will time out furthest in the future will
     * do so at pt_tq.epoch + pt_tq.op->timeout.
     *
     * Subsequently earlier timeouts are computed based on the latter
     * knowledge by subracting the timeout deltas that are stored in
     * the operation list. There are operation[n]->timeout ticks
     * between the expiration of operation[n-1] and operation[n].e e
     *
     * Therefore, the operation[n-1] will expire operation[n]->timeout
     * ticks prior to operation[n].
     *
     * This should be easy!
     */
    t_op = pt_tq.op;                      /* running pointer to queued op */
    op_tmo = now + op->timeout;           /* that's in absolute ticks */
    qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */

    do {
      /*
       * If 'op' expires later than t_op, then insert 'op' just
       * ahead of t_op. Otherwise, compute when operation[n-1]
       * expires and try again.
       *
       * The actual different between the expiriation of 'op'
       * and the current operation what becomes the new operaton's
       * timeout interval. That interval is also subtracted from
       * the interval of the operation immediately following where
       * we stick 'op' (unless the next one isn't timed). The new
       * timeout assigned to 'op' takes into account the values of
       * now() and when the previous intervals were compured.
       */
      delta = op_tmo - qd_tmo;
      if (delta >= 0) {
        op->timeout += (now - pt_tq.epoch);
        goto done;
      }

      qd_tmo -= t_op->timeout; /* previous operaton expiration */
      t_op = t_op->prev;       /* point to previous operation */
      if (NULL != t_op) {
        qd_tmo += t_op->timeout;
      }
    } while (NULL != t_op);

    /*
     * If we got here we backed off the head of the list. That means that
     * this timed entry has to go at the head of the list. This is just
     * about like having an empty timer list.
     */
    delta = op->timeout; /* $$$ is this right? */
  }

done:

  /*
   * Insert 'op' into the queue just after t_op or if t_op is null,
   * at the head of the list.
   *
   * If t_op is NULL, the list is currently empty and this is pretty
   * easy.
   */
  if (NULL == t_op) {
    op->prev = NULL;
    op->next = pt_tq.head;
    pt_tq.head = op;
    if (NULL == pt_tq.tail) {
      pt_tq.tail = op;
    } else {
      op->next->prev = op;
    }
  } else {
    op->prev = t_op;
    op->next = t_op->next;
    if (NULL != op->prev) {
      op->prev->next = op;
    }
    if (NULL != op->next) {
      op->next->prev = op;
    }
    if (t_op == pt_tq.tail) {
      pt_tq.tail = op;
    }
  }

  /*
   * Are we adjusting our epoch, etc? Are we replacing
   * what was previously the element due to expire furthest
   * out in the future? Is this even a timed operation?
   */
  if (PR_INTERVAL_NO_TIMEOUT != op->timeout) {
    if ((NULL == pt_tq.op)     /* we're the one and only */
        || (t_op == pt_tq.op)) /* we're replacing */
    {
      pt_tq.op = op;
      pt_tq.epoch = now;
    }
  }

  pt_tq.op_count += 1;

} /* pt_InsertTimedInternal */

/*
 * function: pt_FinishTimed
 *
 * Takes the finished operation out of the timed queue. It
 * notifies the initiating thread that the opertions is
 * complete and returns to the caller the value of the next
 * operation in the list (or NULL).
 */
static pt_Continuation* pt_FinishTimedInternal(pt_Continuation* op) {
  pt_Continuation* next;

  /* remove this one from the list */
  if (NULL == op->prev) {
    pt_tq.head = op->next;
  } else {
    op->prev->next = op->next;
  }
  if (NULL == op->next) {
    pt_tq.tail = op->prev;
  } else {
    op->next->prev = op->prev;
  }

  /* did we happen to hit the timed op? */
  if (op == pt_tq.op) {
    pt_tq.op = op->prev;
  }

  next = op->next;
  op->next = op->prev = NULL;
  op->status = pt_continuation_done;

  pt_tq.op_count -= 1;
#if defined(DEBUG)
  pt_debug.continuationsServed += 1;
#endif
  PR_NotifyCondVar(op->complete);

  return next;
} /* pt_FinishTimedInternal */

static void ContinuationThread(void* arg) {
  /* initialization */
  fd_set readSet, writeSet, exceptSet;
  struct timeval tv;
  SOCKET* pollingList = 0;          /* list built for polling */
  PRIntn pollingListUsed;           /* # entries used in the list */
  PRIntn pollingListNeeded;         /* # entries needed this time */
  PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
  PRIntervalTime mx_select_ticks =
      PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);

  /* do some real work */
  while (1) {
    PRIntn rv;
    PRStatus status;
    PRIntn pollIndex;
    pt_Continuation* op;
    PRIntervalTime now = PR_IntervalNow();
    PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;

    PR_Lock(pt_tq.ml);
    while (NULL == pt_tq.head) {
      status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
      if ((PR_FAILURE == status) &&
          (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
        break;
      }
    }
    pollingListNeeded = pt_tq.op_count;
    PR_Unlock(pt_tq.ml);

    /* Okay. We're history */
    if ((PR_FAILURE == status) &&
        (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
      break;
    }

    /*
     * We are not holding the pt_tq.ml lock now, so more items may
     * get added to pt_tq during this window of time.  We hope
     * that 10 more spaces in the polling list should be enough.
     */

    FD_ZERO(&readSet);
    FD_ZERO(&writeSet);
    FD_ZERO(&exceptSet);
    pollingListNeeded += 10;
    if (pollingListNeeded > pollingSlotsAllocated) {
      if (NULL != pollingList) {
        PR_DELETE(pollingList);
      }
      pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
      PR_ASSERT(NULL != pollingList);
      pollingSlotsAllocated = pollingListNeeded;
    }

#if defined(DEBUG)
    if (pollingListNeeded > pt_debug.pollingListMax) {
      pt_debug.pollingListMax = pollingListUsed;
    }
#endif

    /*
     * Build up a polling list.
     * This list is sorted on time. Operations that have been
     * interrupted are completed and not included in the list.
     * There is an assertion that the operation is in progress.
     */
    pollingListUsed = 0;
    PR_Lock(pt_tq.ml);

    for (op = pt_tq.head; NULL != op;) {
      if (pt_continuation_abort == op->status) {
        op->result.code = -1;
        op->syserrno = WSAEINTR;
        op = pt_FinishTimedInternal(op);
      } else {
        PR_ASSERT(pt_continuation_done != op->status);
        op->status = pt_continuation_inprogress;
        if (op->event & PR_POLL_READ) {
          FD_SET(op->arg1.osfd, &readSet);
        }
        if (op->event & PR_POLL_WRITE) {
          FD_SET(op->arg1.osfd, &writeSet);
        }
        if (op->event & PR_POLL_EXCEPT) {
          FD_SET(op->arg1.osfd, &exceptSet);
        }
        pollingList[pollingListUsed] = op->arg1.osfd;
        pollingListUsed += 1;
        if (pollingListUsed == pollingSlotsAllocated) {
          break;
        }
        op = op->next;
      }
    }

    PR_Unlock(pt_tq.ml);

    /*
     * If 'op' isn't NULL at this point, then we didn't get to
     * the end of the list. That means that more items got added
     * to the list than we anticipated. So, forget this iteration,
     * go around the horn again.
     * One would hope this doesn't happen all that often.
     */
    if (NULL != op) {
#if defined(DEBUG)
      pt_debug.predictionsFoiled += 1; /* keep track */
#endif
      continue; /* make it rethink things */
    }

    /* there's a chance that all ops got blown away */
    if (NULL == pt_tq.head) {
      continue;
    }
    /* if not, we know this is the shortest timeout */
    timeout = pt_tq.head->timeout;

    /*
     * We don't want to wait forever on this poll. So keep
     * the interval down. The operations, if they are timed,
     * still have to timeout, while those that are not timed
     * should persist forever. But they may be aborted. That's
     * what this anxiety is all about.
     */
    if (timeout > mx_select_ticks) {
      timeout = mx_select_ticks;
    }

    if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout) {
      pt_tq.head->timeout -= timeout;
    }
    tv.tv_sec = PR_IntervalToSeconds(timeout);
    tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;

    rv = select(0, &readSet, &writeSet, &exceptSet, &tv);

    if (0 == rv) /* poll timed out - what about leading op? */
    {
      if (0 == pt_tq.head->timeout) {
        /*
         * The leading element of the timed queue has timed
         * out. Get rid of it. In any case go around the
         * loop again, computing the polling list, checking
         * for interrupted operations.
         */
        PR_Lock(pt_tq.ml);
        do {
          pt_tq.head->result.code = -1;
          pt_tq.head->syserrno = WSAETIMEDOUT;
          op = pt_FinishTimedInternal(pt_tq.head);
        } while ((NULL != op) && (0 == op->timeout));
        PR_Unlock(pt_tq.ml);
      }
      continue;
    }

    if (-1 == rv && (WSAGetLastError() == WSAEINTR ||
                     WSAGetLastError() == WSAEINPROGRESS)) {
      continue; /* go around the loop again */
    }

    /*
     * select() says that something in our list is ready for some more
     * action or is an invalid fd. Find it, load up the operation and
     * see what happens.
     */

    PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);

    /*
     * $$$ There's a problem here. I'm running the operations list
     * and I'm not holding any locks. I don't want to hold the lock
     * and do the operation, so this is really messed up..
     *
     * This may work out okay. The rule is that only this thread,
     * the continuation thread, can remove elements from the list.
     * Therefore, the list is at worst, longer than when we built
     * the polling list.
     */
    op = pt_tq.head;
    for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex) {
      PRInt16 revents = 0;

      PR_ASSERT(NULL != op);

      /*
       * This one wants attention. Redo the operation.
       * We know that there can only be more elements
       * in the op list than we knew about when we created
       * the poll list. Therefore, we might have to skip
       * a few ops to find the right one to operation on.
       */
      while (pollingList[pollIndex] != op->arg1.osfd) {
        op = op->next;
        PR_ASSERT(NULL != op);
      }

      if (FD_ISSET(op->arg1.osfd, &readSet)) {
        revents |= PR_POLL_READ;
      }
      if (FD_ISSET(op->arg1.osfd, &writeSet)) {
        revents |= PR_POLL_WRITE;
      }
      if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
        revents |= PR_POLL_EXCEPT;
      }

      /*
       * Sip over all those not in progress. They'll be
       * pruned next time we build a polling list. Call
       * the continuation function. If it reports completion,
       * finish off the operation.
       */
      if (revents && (pt_continuation_inprogress == op->status) &&
          (op->function(op, revents))) {
        PR_Lock(pt_tq.ml);
        op = pt_FinishTimedInternal(op);
        PR_Unlock(pt_tq.ml);
      }
    }
  }
  if (NULL != pollingList) {
    PR_DELETE(pollingList);
  }
} /* ContinuationThread */

static int pt_Continue(pt_Continuation* op) {
  PRStatus rv;
  /* Finish filling in the blank slots */
  op->status = pt_continuation_sumbitted;
  op->complete = PR_NewCondVar(pt_tq.ml);

  PR_Lock(pt_tq.ml); /* we provide the locking */

  pt_InsertTimedInternal(op); /* insert in the structure */

  PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */

  while (pt_continuation_done != op->status) /* wait for completion */
  {
    rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
    /*
     * If we get interrupted, we set state the continuation thread will
     * see and allow it to finish the I/O operation w/ error. That way
     * the rule that only the continuation thread is removing elements
     * from the list is still valid.
     *
     * Don't call interrupt on the continuation thread. That'll just
     * piss him off. He's cycling around at least every mx_select_ticks
     * anyhow and should notice the request in there.
     */
    if ((PR_FAILURE == rv) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) {
      op->status = pt_continuation_abort; /* our status */
    }
  }

  PR_Unlock(pt_tq.ml); /* we provide the locking */

  PR_DestroyCondVar(op->complete);

  return op->result.code; /* and the primary answer */
} /* pt_Continue */

static PRBool pt_sendto_cont(pt_Continuation* op, PRInt16 revents) {
  PRIntn bytes =
      sendto(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
             (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
  op->syserrno = WSAGetLastError();
  if (bytes > 0) /* this is progress */
  {
    char* bp = op->arg2.buffer;
    bp += bytes; /* adjust the buffer pointer */
    op->arg2.buffer = bp;
    op->result.code += bytes; /* accumulate the number sent */
    op->arg3.amount -= bytes; /* and reduce the required count */
    return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
  } else
    return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ? PR_FALSE
                                                               : PR_TRUE;
} /* pt_sendto_cont */

static PRBool pt_recvfrom_cont(pt_Continuation* op, PRInt16 revents) {
  PRIntn addr_len = sizeof(*(op->arg5.addr));
  op->result.code =
      recvfrom(op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
               (struct sockaddr*)op->arg5.addr, &addr_len);
  op->syserrno = WSAGetLastError();
  return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno))
             ? PR_FALSE
             : PR_TRUE;
} /* pt_recvfrom_cont */

static PRInt32 pt_SendTo(SOCKET osfd, const void* buf, PRInt32 amount,
                         PRInt32 flags, const PRNetAddr* addr, PRIntn addrlen,
                         PRIntervalTime timeout) {
  PRInt32 bytes = -1, err;
  PRBool fNeedContinue = PR_FALSE;

  bytes = sendto(osfd, buf, amount, flags, (struct sockaddr*)addr,
                 PR_NETADDR_SIZE(addr));
  if (bytes == -1) {
    if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
      fNeedContinue = PR_TRUE;
    } else {
      _PR_MD_MAP_SENDTO_ERROR(err);
    }
  }
  if (fNeedContinue == PR_TRUE) {
    pt_Continuation op;
    op.arg1.osfd = osfd;
    op.arg2.buffer = (void*)buf;
    op.arg3.amount = amount;
    op.arg4.flags = flags;
    op.arg5.addr = (PRNetAddr*)addr;
    op.timeout = timeout;
    op.result.code = 0; /* initialize the number sent */
    op.function = pt_sendto_cont;
    op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
    bytes = pt_Continue(&op);
    if (bytes < 0) {
      WSASetLastError(op.syserrno);
      _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
    }
  }
  return bytes;
} /* pt_SendTo */

static PRInt32 pt_RecvFrom(SOCKET osfd, void* buf, PRInt32 amount,
                           PRInt32 flags, PRNetAddr* addr, PRIntn* addr_len,
                           PRIntervalTime timeout) {
  PRInt32 bytes = -1, err;
  PRBool fNeedContinue = PR_FALSE;

  bytes = recvfrom(osfd, buf, amount, flags, (struct sockaddr*)addr, addr_len);
  if (bytes == -1) {
    if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
      fNeedContinue = PR_TRUE;
    } else {
      _PR_MD_MAP_RECVFROM_ERROR(err);
    }
  }

  if (fNeedContinue == PR_TRUE) {
    pt_Continuation op;
    op.arg1.osfd = osfd;
    op.arg2.buffer = buf;
    op.arg3.amount = amount;
    op.arg4.flags = flags;
    op.arg5.addr = addr;
    op.timeout = timeout;
    op.function = pt_recvfrom_cont;
    op.event = PR_POLL_READ | PR_POLL_EXCEPT;
    bytes = pt_Continue(&op);
    if (bytes < 0) {
      WSASetLastError(op.syserrno);
      _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
    }
  }
  return bytes;
} /* pt_RecvFrom */
