home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / md / windows / ntio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  116.6 KB  |  3,777 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  * 
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  * 
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* Windows NT IO module
  20.  *
  21.  * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
  22.  * For LOCAL_SCOPE threads, we're using NT fibers.  For GLOBAL_SCOPE threads
  23.  * we're using NT-native threads.
  24.  *
  25.  * When doing IO, we want to use completion ports for optimal performance 
  26.  * with fibers.  But if we use completion ports for all IO, it is difficult
  27.  * to project a blocking model with GLOBAL_SCOPE threads.  To handle this
  28.  * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
  29.  * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
  30.  * threads because it means extra context switches, which are really slow
  31.  * on NT...  Since we're using a single completion port, some IO will
  32.  * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
  33.  * extra context switching; but I don't think there is anything I can do
  34.  * about it.
  35.  */
  36.  
  37. #include "primpl.h"
  38. #include <direct.h>
  39.  
  40. static HANDLE                _pr_completion_port;
  41. static PRThread             *_pr_io_completion_thread;
  42.  
  43. #define RECYCLE_SIZE 512
  44. static struct _MDLock        _pr_recycle_lock;
  45. static PRInt32               _pr_recycle_array[RECYCLE_SIZE];
  46. static PRInt32               _pr_recycle_tail = 0; 
  47.  
  48. __declspec(thread) PRThread *_pr_io_restarted_io = NULL;
  49. PRBool                       _nt_version_gets_lockfile_completion;
  50.  
  51. struct _MDLock               _pr_ioq_lock;
  52. extern _MDLock               _nt_idleLock;
  53. extern PRCList               _nt_idleList;
  54. extern PRUint32              _nt_idleCount;
  55.  
  56. #define CLOSE_TIMEOUT   PR_SecondsToInterval(5)
  57.  
  58. /*
  59.  * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
  60.  * We store the value in a PRTime variable for convenience.
  61.  * This constant is used by _PR_FileTimeToPRTime().
  62.  */
  63. static const PRTime _pr_filetime_offset = 116444736000000000i64;
  64.  
  65. #define _NEED_351_FILE_LOCKING_HACK
  66. #ifdef _NEED_351_FILE_LOCKING_HACK
  67. #define _PR_LOCAL_FILE 1
  68. #define _PR_REMOTE_FILE 2
  69. PRBool IsFileLocalInit();
  70. PRInt32 IsFileLocal(HANDLE hFile);
  71. #endif /* _NEED_351_FILE_LOCKING_HACK */
  72.  
  73. static PRInt32 _md_Associate(HANDLE);
  74. static PRInt32 _md_MakeNonblock(HANDLE);
  75.  
  76. /* The _nt_use_async flag is used to prevent nspr from using any async io.
  77.  * this is a temporary hack.  Don't learn to rely on it.
  78.  */
  79. static int _nt_use_async = 1;
  80. PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime);
  81. PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
  82. PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
  83. PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime);
  84. PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
  85. PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
  86.  
  87. /*
  88.  * UDP support
  89.  * 
  90.  * UDP is supported on NT by the continuation thread mechanism.
  91.  * The code is borrowed from ptio.c in pthreads nspr, hence the
  92.  * PT and pt prefixes.  This mechanism is in fact general and
  93.  * not limited to UDP.  For now, only UDP's recvfrom and sendto
  94.  * go through the continuation thread if they get WSAEWOULDBLOCK
  95.  * on first try.  Recv and send on a connected UDP socket still
  96.  * goes through asychronous io.
  97.  */
  98.  
  99. #define PT_DEFAULT_SELECT_MSEC 100
  100.  
  101. typedef struct pt_Continuation pt_Continuation;
  102. typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
  103.  
  104. typedef enum pr_ContuationStatus
  105. {
  106.     pt_continuation_sumbitted,
  107.     pt_continuation_inprogress,
  108.     pt_continuation_abort,
  109.     pt_continuation_done
  110. } pr_ContuationStatus;
  111.  
  112. struct pt_Continuation
  113. {
  114.     /* These objects are linked in ascending timeout order */
  115.     pt_Continuation *next, *prev;           /* self linked list of these things */
  116.  
  117.     /* The building of the continuation operation */
  118.     ContinuationFn function;                /* what function to continue */
  119.     union { SOCKET osfd; } arg1;            /* #1 - the op's fd */
  120.     union { void* buffer; } arg2;           /* #2 - primary transfer buffer */
  121.     union { PRIntn amount; } arg3;          /* #3 - size of 'buffer' */
  122.     union { PRIntn flags; } arg4;           /* #4 - read/write flags */
  123.     union { PRNetAddr *addr; } arg5;        /* #5 - send/recv address */
  124.     
  125.     PRIntervalTime timeout;                 /* representation of the timeout */
  126.  
  127.     PRIntn event;                           /* flags for select()'s events */
  128.  
  129.     /*
  130.     ** The representation and notification of the results of the operation.
  131.     ** These function can either return an int return code or a pointer to
  132.     ** some object.
  133.     */
  134.     union { PRIntn code; void *object; } result;
  135.  
  136.     PRIntn syserrno;                        /* in case it failed, why (errno) */
  137.     pr_ContuationStatus status;             /* the status of the operation */
  138.     PRCondVar *complete;                    /* to notify the initiating thread */
  139. };
  140.  
  141. static struct pt_TimedQueue
  142. {
  143.     PRLock *ml;                             /* a little protection */
  144.     PRThread *thread;                       /* internal thread's identification */
  145.     PRCondVar *new_op;                      /* new operation supplied */
  146.     PRCondVar *finish_op;                   /* an existing operation finished */
  147.     PRUintn op_count;                       /* number of operations in the list */
  148.     pt_Continuation *head, *tail;           /* head/tail of list of operations */
  149.  
  150.     pt_Continuation *op;                    /* timed operation furthest in future */
  151.     PRIntervalTime epoch;                   /* the epoch of 'timed' */
  152. } pt_tq;
  153.  
  154. #if defined(DEBUG)
  155. static struct pt_debug_s
  156. {
  157.     PRIntn predictionsFoiled;
  158.     PRIntn pollingListMax;
  159.     PRIntn continuationsServed;
  160. } pt_debug;
  161. #endif  /* DEBUG */
  162.  
  163. static void ContinuationThread(void *arg);
  164. static PRInt32 pt_SendTo(
  165.     SOCKET osfd, const void *buf,
  166.     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
  167.     PRIntn addrlen, PRIntervalTime timeout);
  168. static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
  169.     PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
  170.  
  171.  
  172. /* The key returned from GetQueuedCompletionStatus() is used to determine what
  173.  * type of completion we have.  We differentiate between IO completions and
  174.  * CVAR completions.
  175.  */
  176. #define KEY_IO              0xaaaaaaaa
  177. #define KEY_CVAR            0xbbbbbbbb
  178.  
  179. PRInt32
  180. _PR_MD_PAUSE_CPU(PRIntervalTime ticks)
  181. {
  182.     int awoken = 0;
  183.     unsigned long bytes, key;
  184.     int rv;
  185.     LPOVERLAPPED olp;
  186.     PRThread *completed_io;
  187.     PRUint32 timeout;
  188.  
  189.     if (_nt_idleCount > 0) {
  190.         PRThread *deadThread;
  191.  
  192.         _MD_LOCK(&_nt_idleLock);
  193.         while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
  194.             deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
  195.             PR_REMOVE_LINK(&deadThread->links);
  196.  
  197.             PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
  198.  
  199.             /* XXXMB - cleanup to do here? */
  200.             if ( !_PR_IS_NATIVE_THREAD(deadThread) ){
  201.                 /* Spinlock while user thread is still running.
  202.                  * There is no way to use a condition variable here. The thread
  203.                  * is dead, and we have to wait until we switch off the dead 
  204.                  * thread before we can kill the fiber completely.
  205.                  */
  206.                 while ( deadThread->no_sched)
  207.                     ;
  208.  
  209.                 DeleteFiber(deadThread->md.fiber_id);
  210.             }
  211.             memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
  212.             if (!deadThread->threadAllocatedOnStack)
  213.                 PR_DELETE(deadThread);
  214.             _nt_idleCount--;
  215.         }
  216.         _MD_UNLOCK(&_nt_idleLock);
  217.     }
  218.  
  219.     if (ticks == PR_INTERVAL_NO_TIMEOUT)
  220. #if 0
  221.         timeout = INFINITE;
  222. #else
  223.     /* temporary hack to poll the runq every 5 seconds because of bug in
  224.      * native threads creating user threads and not poking the right cpu.
  225.      */
  226.         timeout = 5000;
  227. #endif
  228.     else 
  229.         timeout = PR_IntervalToMilliseconds(ticks);
  230.  
  231.     /*
  232.      * The idea of looping here is to complete as many IOs as possible before
  233.      * returning.  This should minimize trips to the idle thread.
  234.      */
  235.     while(1) {
  236.         rv = GetQueuedCompletionStatus(
  237.                 _pr_completion_port,
  238.                 &bytes,
  239.                 &key,
  240.                 &olp,
  241.                 timeout); 
  242.         if (rv == 0 && olp == NULL) {
  243.             /* Error in GetQueuedCompetionStatus */
  244.             if (GetLastError() != WAIT_TIMEOUT) {
  245.                 /* ARGH - what can we do here? Log an error? XXXMB */
  246.                 return -1;
  247.             } else {
  248.                 /* If awoken == 0, then we just had a timeout */
  249.                 return awoken;
  250.             }
  251.         }
  252.  
  253.         if (olp == NULL) 
  254.             return 0;
  255.  
  256.         completed_io = _PR_THREAD_MD_TO_PTR(olp);
  257.         completed_io->md.blocked_io_status = rv;
  258.         if (rv == 0)
  259.             completed_io->md.blocked_io_error = GetLastError();
  260.         completed_io->md.blocked_io_bytes = bytes;
  261.  
  262.         if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
  263.             int pri = completed_io->priority;
  264.             _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
  265.  
  266.             /* The KEY_CVAR notification only occurs when a native thread
  267.              * is notifying a user thread.  For user-user notifications
  268.              * the wakeup occurs by having the notifier place the thread 
  269.              * on the runq directly; for native-native notifications the
  270.              * wakeup occurs by calling ReleaseSemaphore.
  271.              */
  272.             if ( key == KEY_CVAR ) {
  273.                 PR_ASSERT(completed_io->io_pending == PR_FALSE || completed_io->io_suspended == PR_TRUE);
  274.  
  275.                 /* Thread has already been deleted from sleepQ */
  276.  
  277.                 /* Switch CPU and add to runQ */
  278.                 completed_io->cpu = lockedCPU;
  279.                 completed_io->state = _PR_RUNNABLE;
  280.                 _PR_RUNQ_LOCK(lockedCPU);
  281.                 _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
  282.                 _PR_RUNQ_UNLOCK(lockedCPU);
  283.             } else {
  284.                 PR_ASSERT(key == KEY_IO);
  285.                 PR_ASSERT(completed_io->io_pending == PR_TRUE);
  286.  
  287.                 _PR_THREAD_LOCK(completed_io);
  288.  
  289.                 completed_io->io_pending = PR_FALSE;
  290.  
  291.                 /* If io_suspended is true, then this IO has already resumed.
  292.                  * We don't need to do anything; because the thread is
  293.                  * already running.
  294.                  */
  295.                 if (completed_io->io_suspended == PR_FALSE) {
  296.                     if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
  297.                         _PR_SLEEPQ_LOCK(completed_io->cpu);
  298.                         _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
  299.                         _PR_SLEEPQ_UNLOCK(completed_io->cpu);
  300.  
  301.                         _PR_THREAD_UNLOCK(completed_io);
  302.  
  303.                         completed_io->cpu = lockedCPU;
  304.                         completed_io->state = _PR_RUNNABLE;
  305.                         _PR_RUNQ_LOCK(lockedCPU);
  306.                         _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
  307.                         _PR_RUNQ_UNLOCK(lockedCPU);
  308.                     } else {
  309.                         _PR_THREAD_UNLOCK(completed_io);
  310.                     }
  311.                 } else {
  312.                     _PR_THREAD_UNLOCK(completed_io);
  313.                 }
  314.             }
  315.         } else {
  316.             int old_count;
  317.             PRBool fNeedRelease = PR_FALSE;
  318.  
  319.             /* For native threads, they are only notified through this loop
  320.              * when completing IO.  So, don't worry about this being a CVAR
  321.              * notification, because that is not possible.
  322.              */
  323.             _PR_THREAD_LOCK(completed_io);
  324.             completed_io->io_pending = PR_FALSE;
  325.             if (completed_io->io_suspended == PR_FALSE) {
  326.                 completed_io->state = _PR_RUNNABLE;
  327.                 fNeedRelease = PR_TRUE;
  328.             }
  329.             _PR_THREAD_UNLOCK(completed_io);
  330.             if (fNeedRelease) {
  331.                 rv = ReleaseSemaphore(completed_io->md.blocked_sema,
  332.                         1, &old_count);
  333.                 PR_ASSERT(0 != rv);
  334.             }
  335.         }
  336.  
  337.         awoken++;
  338.         timeout = 0;   /* Don't block on subsequent trips through the loop */
  339.     }
  340.  
  341.     /* never reached */
  342.     return 0;
  343. }
  344.  
  345. PRStatus
  346. _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
  347. {
  348.     DWORD rv;
  349.  
  350.     if ( thread->flags & _PR_GLOBAL_SCOPE ) {
  351.         PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
  352.             INFINITE : PR_IntervalToMilliseconds(ticks);
  353.         rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
  354.         switch(rv) {
  355.             case WAIT_OBJECT_0:
  356.                 return PR_SUCCESS;
  357.                 break;
  358.             case WAIT_TIMEOUT:
  359.                 _PR_THREAD_LOCK(thread);
  360.                 if (thread->state == _PR_IO_WAIT) {
  361.                     if (thread->io_pending == PR_TRUE) {
  362.                         thread->io_suspended = PR_TRUE;
  363.                         _PR_THREAD_UNLOCK(thread);
  364.                     } else {
  365.                         /* The IO completed just at the same time the timeout
  366.                          * occurred.  This led to us being notified twice.
  367.                          * call WaitForSingleObject() to clear the semaphore.
  368.                          */
  369.                         _PR_THREAD_UNLOCK(thread);
  370.                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
  371.                         PR_ASSERT(rv == WAIT_OBJECT_0);
  372.                     }
  373.                 } else {
  374.                     if (thread->wait.cvar != NULL) {
  375.                         thread->wait.cvar = NULL;
  376.                         thread->state = _PR_RUNNING;
  377.                         _PR_THREAD_UNLOCK(thread);
  378.                     } else {
  379.                         /* The CVAR was notified just as the timeout
  380.                          * occurred.  This led to us being notified twice.
  381.                          * call WaitForSingleObject() to clear the semaphore.
  382.                          */
  383.                         _PR_THREAD_UNLOCK(thread);
  384.                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
  385.                         PR_ASSERT(rv == WAIT_OBJECT_0);
  386.                     }
  387.                 }
  388.                 return PR_SUCCESS;
  389.                 break;
  390.             default:
  391.                 return PR_FAILURE;
  392.                 break;
  393.         }
  394.     } else {
  395.         PRInt32 is;
  396.  
  397.         /* XXXMB - This is barely safe, but works.   We should find a 
  398.          * way to make all callers of PR_MD_WAIT zero the overlapped buffer
  399.          * themselves...
  400.          */
  401.         if (thread->state != _PR_IO_WAIT)
  402.             memset(&(thread->md.overlapped), 0, sizeof(OVERLAPPED));
  403.         if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSOFF(is);
  404.         _PR_MD_SWITCH_CONTEXT(thread);
  405.     }
  406.  
  407.     return PR_SUCCESS;
  408. }
  409.  
  410. static PRStatus
  411. _NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
  412. {
  413.     PRBool fWait = PR_TRUE;
  414.  
  415.     if (!_PR_IS_NATIVE_THREAD(thread))  {
  416.  
  417.         _PR_THREAD_LOCK(thread);
  418.  
  419.         /* The IO may have already completed; if so, don't add to sleepQ, 
  420.          * since we are already on the runQ!
  421.          */
  422.         if (thread->io_pending == PR_TRUE) {
  423.             _PR_SLEEPQ_LOCK(thread->cpu);
  424.             _PR_ADD_SLEEPQ(thread, timeout);
  425.             _PR_SLEEPQ_UNLOCK(thread->cpu);
  426.         } else
  427.             fWait = PR_FALSE;
  428.         _PR_THREAD_UNLOCK(thread);
  429.     }
  430.     if (fWait)
  431.         return _PR_MD_WAIT(thread, timeout);
  432.     else
  433.         return PR_SUCCESS;
  434. }
  435.  
  436. /*
  437.  * Unblock threads waiting for I/O
  438.  * used when interrupting threads
  439.  *
  440.  * NOTE: The thread lock should held when this function is called.
  441.  * On return, the thread lock is released.
  442.  */
  443. void _PR_Unblock_IO_Wait(PRThread *thr)
  444. {
  445.     PRStatus rv;
  446.     _PRCPU *cpu = thr->cpu;
  447.  
  448.     PR_ASSERT(thr->state == _PR_IO_WAIT);
  449.     thr->io_suspended = PR_TRUE;
  450.     thr->state = _PR_RUNNABLE;
  451.  
  452.     if (!_PR_IS_NATIVE_THREAD(thr)) {
  453.         PRThread *me = _PR_MD_CURRENT_THREAD();
  454.         PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
  455.         _PR_SLEEPQ_LOCK(cpu);
  456.         _PR_DEL_SLEEPQ(thr, PR_TRUE);
  457.         _PR_SLEEPQ_UNLOCK(cpu);
  458.  
  459.         PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
  460.         _PR_AddThreadToRunQ(me, thr);
  461.     }
  462.     _PR_THREAD_UNLOCK(thr);
  463.     rv = _PR_MD_WAKEUP_WAITER(thr);
  464.     PR_ASSERT(PR_SUCCESS == rv);
  465. }
  466.  
  467. /* Resume an outstanding IO; requires that after the switch, we disable */
  468. static PRStatus
  469. _NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
  470. {
  471.     PRBool fWait = PR_TRUE;
  472.  
  473.     if (!_PR_IS_NATIVE_THREAD(thread)) {
  474.         _pr_io_restarted_io = thread;
  475.     } else {
  476.         _PR_THREAD_LOCK(thread);
  477.         if (!thread->io_pending)
  478.             fWait = PR_FALSE;
  479.         thread->io_suspended = PR_FALSE;
  480.             
  481.         _PR_THREAD_UNLOCK(thread);
  482.     }
  483.     /* We don't put ourselves back on the sleepQ yet; until we 
  484.      * set the suspended bit to false, we can't do that.  Just save
  485.      * the sleep time here, and then continue.  The restarted_io handler
  486.      * will add us to the sleepQ if needed.
  487.      */
  488.     thread->sleep = ticks;
  489.  
  490.     if (fWait)
  491.         return _PR_MD_WAIT(thread, ticks);
  492.     return PR_SUCCESS;
  493. }
  494.  
  495. PRStatus
  496. _PR_MD_WAKEUP_WAITER(PRThread *thread)
  497. {
  498.     if (thread == NULL) {
  499.         /* If thread is NULL, we aren't waking a thread, we're just poking
  500.          * idle thread 
  501.          */
  502.         if ( PostQueuedCompletionStatus(_pr_completion_port, 0, 
  503.             KEY_CVAR, NULL) == FALSE) 
  504.             return PR_FAILURE;
  505.         return PR_SUCCESS;
  506.     }
  507.  
  508.     if ( _PR_IS_NATIVE_THREAD(thread) ) {
  509.         if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
  510.             return PR_FAILURE;
  511.         else
  512.             return PR_SUCCESS;
  513.     } else {
  514.         PRThread *me = _PR_MD_CURRENT_THREAD();
  515.  
  516.         /* When a Native thread has to awaken a user thread, it has to poke
  517.          * the completion port because all user threads might be idle, and
  518.          * thus the CPUs are just waiting for a completion.  
  519.          *
  520.          * XXXMB - can we know when we are truely idle (and not checking 
  521.          *         the runq)?
  522.          */
  523.         if (_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) {
  524.             /* The thread should not be in any queue */
  525.             PR_ASSERT(thread->queueCount == 0);
  526.             if ( PostQueuedCompletionStatus(_pr_completion_port, 0, 
  527.                 KEY_CVAR, &(thread->md.overlapped)) == FALSE) 
  528.                 return PR_FAILURE;
  529.         }
  530.         return PR_SUCCESS;
  531.     }
  532. }
  533.  
  534. void
  535. _PR_MD_INIT_IO()
  536. {
  537.     WORD WSAVersion = 0x0101;
  538.     WSADATA WSAData;
  539.     OSVERSIONINFO OSversion;
  540.  
  541.     WSAStartup(    WSAVersion, &WSAData );
  542.                                                       
  543.     _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 
  544.                                                  NULL, 
  545.                                                  0, 
  546.                                                  0);
  547.  
  548.     _MD_NEW_LOCK(&_pr_recycle_lock);
  549.     _MD_NEW_LOCK(&_pr_ioq_lock);
  550.  
  551.     OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  552.     if (GetVersionEx(&OSversion)) {
  553.         _nt_version_gets_lockfile_completion = PR_FALSE;
  554.         if (OSversion.dwMajorVersion >= 4) {
  555.             _nt_version_gets_lockfile_completion = PR_TRUE;
  556.         }
  557.     } else 
  558.         PR_ASSERT(0);
  559.  
  560.     IsFileLocalInit();
  561.  
  562.     /*
  563.      * UDP support: start up the continuation thread
  564.      */
  565.  
  566.     pt_tq.op_count = 0;
  567.     pt_tq.head = pt_tq.tail = NULL;
  568.     pt_tq.ml = PR_NewLock();
  569.     PR_ASSERT(NULL != pt_tq.ml);
  570.     pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
  571.     PR_ASSERT(NULL != pt_tq.new_op);
  572. #if defined(DEBUG)
  573.     memset(&pt_debug, 0, sizeof(struct pt_debug_s));
  574. #endif
  575.  
  576.     pt_tq.thread = PR_CreateThread(
  577.         PR_SYSTEM_THREAD, ContinuationThread, NULL,
  578.         PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
  579.  
  580.     PR_ASSERT(NULL != pt_tq.thread);
  581.  
  582. #ifdef DEBUG
  583.     /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
  584.     {
  585.         SYSTEMTIME systime;
  586.         union {
  587.            PRTime prt;
  588.            FILETIME ft;
  589.         } filetime;
  590.         BOOL rv;
  591.  
  592.         systime.wYear = 1970;
  593.         systime.wMonth = 1;
  594.         /* wDayOfWeek is ignored */
  595.         systime.wDay = 1;
  596.         systime.wHour = 0;
  597.         systime.wMinute = 0;
  598.         systime.wSecond = 0;
  599.         systime.wMilliseconds = 0;
  600.  
  601.         rv = SystemTimeToFileTime(&systime, &filetime.ft);
  602.         PR_ASSERT(0 != rv);
  603.         PR_ASSERT(filetime.prt == _pr_filetime_offset);
  604.     }
  605. #endif /* DEBUG */
  606. }
  607.  
  608. /* --- SOCKET IO --------------------------------------------------------- */
  609.  
  610. /* _md_get_recycled_socket()
  611.  * Get a socket from the recycle bin; if no sockets are in the bin,
  612.  * create one.  The socket will be passed to AcceptEx() as the
  613.  * second argument.
  614.  */
  615. static SOCKET
  616. _md_get_recycled_socket()
  617. {
  618.     SOCKET rv;
  619.     int af = AF_INET;
  620.  
  621.     _MD_LOCK(&_pr_recycle_lock);
  622.     if (_pr_recycle_tail) {
  623.         _pr_recycle_tail--;
  624.         rv = _pr_recycle_array[_pr_recycle_tail];
  625.         _MD_UNLOCK(&_pr_recycle_lock);
  626.         return rv;
  627.     }
  628.     _MD_UNLOCK(&_pr_recycle_lock);
  629.  
  630. #ifdef _PR_INET6
  631.     if (_pr_ipv6_enabled) {
  632.         af = AF_INET6;
  633.     }
  634. #endif
  635.     rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
  636.     if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
  637.         closesocket(rv);
  638.         return INVALID_SOCKET;
  639.     }
  640.     return rv;
  641. }
  642.  
  643. /* _md_put_recycled_socket()
  644.  * Add a socket to the recycle bin.
  645.  */
  646. static void
  647. _md_put_recycled_socket(SOCKET newsock)
  648. {
  649.     PR_ASSERT(_pr_recycle_tail >= 0);
  650.  
  651.     _MD_LOCK(&_pr_recycle_lock);
  652.     if (_pr_recycle_tail < RECYCLE_SIZE) {
  653.         _pr_recycle_array[_pr_recycle_tail] = newsock;
  654.         _pr_recycle_tail++;
  655.         _MD_UNLOCK(&_pr_recycle_lock);
  656.     } else {
  657.         _MD_UNLOCK(&_pr_recycle_lock);
  658.         closesocket(newsock);
  659.     }
  660.  
  661.     return;
  662. }
  663.  
  664. /* _md_Associate()
  665.  * Associates a file with the completion port.
  666.  * Returns 0 on failure, 1 on success.
  667.  */
  668. static PRInt32
  669. _md_Associate(HANDLE file)
  670. {
  671.     HANDLE port;
  672.  
  673.     port = CreateIoCompletionPort((HANDLE)file, 
  674.                                     _pr_completion_port, 
  675.                                     KEY_IO,
  676.                                     0);
  677.  
  678.     /* XXX should map error codes on failures */
  679.     return (port == _pr_completion_port);
  680. }
  681.  
  682. /*
  683.  * _md_MakeNonblock()
  684.  * Make a socket nonblocking.
  685.  * Returns 0 on failure, 1 on success.
  686.  */
  687. static PRInt32
  688. _md_MakeNonblock(HANDLE file)
  689. {
  690.     int rv;
  691.     u_long one = 1;
  692.  
  693.     rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
  694.     /* XXX should map error codes on failures */
  695.     return (rv == 0);
  696. }
  697.  
  698. static int missing_completions = 0;
  699. static int max_wait_loops = 0;
  700.  
  701. static PRInt32
  702. _NT_IO_ABORT(PRInt32 sock)
  703. {
  704.     PRThread *me = _PR_MD_CURRENT_THREAD();
  705.     PRBool fWait;
  706.     PRInt32 rv;
  707.     int loop_count;
  708.  
  709.     /* This is a clumsy way to abort the IO, but it is all we can do.
  710.      * It looks a bit racy, but we handle all the cases. 
  711.      * case 1:  IO completes before calling closesocket
  712.      *     case 1a:  fWait is set to PR_FALSE
  713.      *           This should e the most likely case.  We'll properly
  714.      *           not wait call _NT_IO_WAIT, since the closesocket()
  715.      *           won't be forcing a completion.
  716.      *     case 1b: fWait is set to PR_TRUE
  717.      *           This hopefully won't happen much.  When it does, this
  718.      *           thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
  719.      *           before cleaning up.
  720.      * case 2:  IO does not complete before calling closesocket
  721.      *     case 2a: IO never completes
  722.      *           This is the likely case.  We'll close it and wait
  723.      *           for the completion forced by the close.  Return should
  724.      *           be immediate.
  725.      *     case 2b: IO completes just after calling closesocket
  726.      *           Since the closesocket is issued, we'll either get a
  727.      *           completion back for the real IO or for the close.  We
  728.      *           don't really care.  It may not even be possible to get
  729.      *           a real completion here.  In any event, we'll awaken
  730.      *           from NT_IO_WAIT immediately.
  731.      */
  732.  
  733.     _PR_THREAD_LOCK(me);
  734.     fWait = me->io_pending;
  735.     if (fWait) {
  736.         /*
  737.          * If there's still I/O pending, it should have already timed
  738.          * out once before this function is called.
  739.          */
  740.         PR_ASSERT(me->io_suspended == PR_TRUE);
  741.  
  742.         /* Set up to wait for I/O completion again */
  743.         me->state = _PR_IO_WAIT;
  744.         me->io_suspended = PR_FALSE;
  745.     }
  746.     _PR_THREAD_UNLOCK(me);
  747.  
  748.     /* Close the socket if there is one */
  749.     if (sock != INVALID_SOCKET) {
  750.         rv = closesocket((SOCKET)sock);
  751.     }
  752.  
  753.     /* If there was I/O pending before the close, wait for it to complete */
  754.     if (fWait) {
  755.  
  756.         /* Wait and wait for the I/O to complete */
  757.         for (loop_count = 0; fWait; ++loop_count) {
  758.  
  759.             _NT_IO_WAIT(me, CLOSE_TIMEOUT);
  760.  
  761.             _PR_THREAD_LOCK(me);
  762.             fWait = me->io_pending;
  763.             if (fWait) {
  764.                 PR_ASSERT(me->io_suspended == PR_TRUE);
  765.                 me->state = _PR_IO_WAIT;
  766.                 me->io_suspended = PR_FALSE;
  767.             }
  768.             _PR_THREAD_UNLOCK(me);
  769.  
  770.             if (loop_count > max_wait_loops) {
  771.                 max_wait_loops = loop_count;
  772.             }
  773.         }
  774.  
  775.         if (loop_count > 1) {
  776.             ++missing_completions;
  777.         }
  778.  
  779.         me->io_pending = PR_FALSE;
  780.         me->state = _PR_RUNNING;
  781.     }
  782.  
  783.     PR_ASSERT(me->io_pending == PR_FALSE);
  784.     me->io_suspended = PR_FALSE;
  785.  
  786.     return rv;
  787. }
  788.  
  789.  
  790. PRInt32
  791. _PR_MD_SOCKET(int af, int type, int flags)
  792. {
  793.     SOCKET sock;
  794.  
  795.     sock = socket(af, type, flags);
  796.  
  797.     if (sock == INVALID_SOCKET) {
  798.         _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
  799.     }
  800.  
  801.     return (PRInt32)sock;
  802. }
  803.  
  804. struct connect_data_s {
  805.     PRInt32 status;
  806.     PRInt32 error;
  807.     PRInt32 osfd;
  808.     struct sockaddr *addr;
  809.     PRUint32 addrlen;
  810.     PRIntervalTime timeout;
  811. };
  812.  
  813. void
  814. _PR_MD_connect_thread(void *cdata)
  815. {
  816.     struct connect_data_s *cd = (struct connect_data_s *)cdata;
  817.  
  818.     cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
  819.  
  820.     if (cd->status == SOCKET_ERROR)
  821.         cd->error = WSAGetLastError();
  822.  
  823.     return;
  824. }
  825.  
  826.  
  827. PRInt32
  828. _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, 
  829.                PRIntervalTime timeout)
  830. {
  831.     PRInt32 osfd = fd->secret->md.osfd;
  832.     PRThread *me = _PR_MD_CURRENT_THREAD();
  833.     PRInt32 rv;
  834.     PRThread *cThread;
  835.     struct connect_data_s cd;
  836.  
  837.     if (!_nt_use_async || fd->secret->nonblocking) {
  838.         PRInt32 rv;
  839.         fd_set wd;
  840.         struct timeval tv, *tvp;
  841.  
  842.         if (!fd->secret->md.io_model_committed) {
  843.             rv = _md_MakeNonblock((HANDLE)osfd);
  844.             PR_ASSERT(0 != rv);
  845.             fd->secret->md.io_model_committed = PR_TRUE;
  846.         }
  847.  
  848.         while ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
  849.             rv = WSAGetLastError();
  850.             if ((!fd->secret->nonblocking) && ((rv == WSAEWOULDBLOCK) ||
  851.                 (rv == WSAEALREADY) ||
  852.                 (rv == WSAEINVAL) /* for winsock1.1, it reports EALREADY as EINVAL */)) {
  853.                 if (timeout == PR_INTERVAL_NO_TIMEOUT)
  854.                     tvp = NULL;
  855.                 else {
  856.                     tv.tv_sec = PR_IntervalToSeconds(timeout);
  857.                     tv.tv_usec = PR_IntervalToMicroseconds(
  858.                     timeout - PR_SecondsToInterval(tv.tv_sec));
  859.                     tvp = &tv;
  860.                 }
  861.  
  862.                 FD_ZERO(&wd);
  863.                 FD_SET((SOCKET)osfd, &wd);
  864.                 rv = select(osfd + 1, NULL, &wd, NULL, tvp);
  865.                 if (rv > 0) {
  866.                     rv = 0;
  867.                 } else if (rv == 0) {
  868.                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  869.                     return(-1);
  870.                 } else if (rv < 0) {
  871.                     rv = WSAGetLastError();
  872.                     _PR_MD_MAP_SELECT_ERROR(rv);
  873.                     return(-1);
  874.                 }
  875.             } else if ((rv == WSAEISCONN)) {
  876.                 /* Success! */
  877.                 return 0;
  878.             } else {
  879.                 _PR_MD_MAP_CONNECT_ERROR(rv);
  880.                 return -1;
  881.             }
  882.         }
  883.         return rv;
  884.     }
  885.  
  886.     /* If we are a native thread, just make the blocking IO call */
  887.     if (_PR_IS_NATIVE_THREAD(me)) {
  888.         rv = connect(osfd, (struct sockaddr *)addr, addrlen);
  889.         if (rv == -1) {
  890.             rv = WSAGetLastError();
  891.             _PR_MD_MAP_CONNECT_ERROR(rv);
  892.             return -1;
  893.         } else
  894.             return rv;
  895.     }
  896.  
  897.     /* NT doesn't provide a nice way to do asynchronous 
  898.      * connect.  The proxy team invented a huge chunk of code which has
  899.      * a single thread multiplexing multiple connect requests via 
  900.      * WSAAsyncSelect().  That is a better solution, but I'm not doing that
  901.      * now.  At this point, just create a real thread to do the work.
  902.      *
  903.      * Rumor has it that on nt3.51, all the WSA library does is create 
  904.      * a thread to call a blocking connect() anyway.  On 4.0 they've fixed
  905.      * that.  -mbelshe
  906.      */
  907.     cd.osfd = osfd;
  908.     cd.addr = (struct sockaddr *)addr;
  909.     cd.addrlen = addrlen;
  910.     cd.timeout = timeout;
  911.     cThread = PR_CreateThread(PR_SYSTEM_THREAD,
  912.                               _PR_MD_connect_thread,
  913.                               (void *)&cd,
  914.                               PR_PRIORITY_NORMAL,
  915.                               PR_GLOBAL_THREAD,
  916.                               PR_JOINABLE_THREAD,
  917.                               0);
  918.  
  919.     if (cThread == NULL) {
  920.         return -1;
  921.     }
  922.  
  923.     PR_JoinThread(cThread);
  924.  
  925.     rv = cd.status;
  926.  
  927.     if (rv == SOCKET_ERROR) {
  928.         _PR_MD_MAP_CONNECT_ERROR(cd.error);
  929.         return -1;
  930.     }
  931.  
  932.     return 0;
  933. }
  934.  
  935. PRInt32
  936. _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
  937. {
  938.     PRInt32 rv;
  939. #if 0
  940.     int one = 1;
  941. #endif
  942.  
  943.     rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
  944.  
  945.     if (rv == SOCKET_ERROR) {
  946.         _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
  947.         return -1;
  948.     }
  949.  
  950. #if 0
  951.     /* Disable nagle- so far unknown if this is good or not...
  952.      */
  953.     rv = setsockopt(fd->secret->md.osfd, 
  954.                     SOL_SOCKET,
  955.                     TCP_NODELAY,
  956.                     (const char *)&one,
  957.                     sizeof(one));
  958.     PR_ASSERT(rv == 0);
  959. #endif
  960.  
  961.     return 0;
  962. }
  963.  
  964. void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 accept_sock, PRInt32 listen_sock)
  965. {
  966.     /* Sockets accept()'d with AcceptEx need to call this setsockopt before
  967.      * calling anything other than ReadFile(), WriteFile(), send(), recv(), 
  968.      * Transmitfile(), and closesocket().  In order to call any other 
  969.      * winsock functions, we have to make this setsockopt call.
  970.      *
  971.      * XXXMB - For the server, we *NEVER* need this in
  972.      * the "normal" code path.  But now we have to call it.  This is a waste
  973.      * of a system call.  We'd like to only call it before calling the 
  974.      * obscure socket calls, but since we don't know at that point what the
  975.      * original socket was (or even if it is still alive) we can't do it
  976.      * at that point... 
  977.      */
  978.     setsockopt((SOCKET)accept_sock, 
  979.                SOL_SOCKET, 
  980.                SO_UPDATE_ACCEPT_CONTEXT,
  981.                (char *)&listen_sock,
  982.                sizeof(listen_sock));
  983.  
  984. }
  985.  
  986. #define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
  987. PRInt32
  988. _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
  989.               PRIntervalTime timeout, PRBool fast, 
  990.               _PR_AcceptTimeoutCallback callback, void *callbackArg)
  991. {
  992.     PRInt32 osfd = fd->secret->md.osfd;
  993.     PRThread *me = _PR_MD_CURRENT_THREAD();
  994.     SOCKET accept_sock;
  995.     int bytes;
  996.     PRNetAddr *Laddr;
  997.     PRNetAddr *Raddr;
  998.     PRUint32 llen, err;
  999.     int rv;
  1000.  
  1001.     if (!_nt_use_async || fd->secret->nonblocking) {
  1002.         if (!fd->secret->md.io_model_committed) {
  1003.             rv = _md_MakeNonblock((HANDLE)osfd);
  1004.             PR_ASSERT(0 != rv);
  1005.             fd->secret->md.io_model_committed = PR_TRUE;
  1006.         }
  1007.         /*
  1008.          * The accepted socket inherits the nonblocking attribute of
  1009.          * the listening socket, so no need to call _md_MakeNonblock().
  1010.          */
  1011.         return _nt_nonblock_accept(fd, (struct sockaddr_in *)raddr, rlen, timeout);
  1012.     }
  1013.  
  1014.     if (!fd->secret->md.io_model_committed) {
  1015.         rv = _md_Associate((HANDLE)osfd);
  1016.         PR_ASSERT(0 != rv);
  1017.         fd->secret->md.io_model_committed = PR_TRUE;
  1018.     }
  1019.  
  1020.     if (!me->md.acceptex_buf) {
  1021.         me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
  1022.         if (!me->md.acceptex_buf) {
  1023.             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
  1024.             return -1;
  1025.         }
  1026.     }
  1027.  
  1028.     accept_sock = _md_get_recycled_socket();
  1029.     if (accept_sock == INVALID_SOCKET)
  1030.         return -1;
  1031.  
  1032.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1033.  
  1034.     PR_ASSERT(me->io_suspended == PR_FALSE);
  1035.  
  1036.     me->io_pending = PR_TRUE;
  1037.     me->io_fd = osfd;
  1038.     me->state = _PR_IO_WAIT;
  1039.     rv = AcceptEx((SOCKET)osfd,
  1040.                   accept_sock,
  1041.                   me->md.acceptex_buf,
  1042.                   0,
  1043.                   INET_ADDR_PADDED,
  1044.                   INET_ADDR_PADDED,
  1045.                   &bytes,
  1046.                   &(me->md.overlapped));
  1047.  
  1048.     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING))  {
  1049.         /* Argh! The IO failed */
  1050.         me->io_pending = PR_FALSE;
  1051.         me->state = _PR_RUNNING;
  1052.         _PR_MD_MAP_ACCEPTEX_ERROR(err);
  1053.         return -1;
  1054.     }
  1055.  
  1056.     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
  1057.         PR_ASSERT(0);
  1058.         return -1;
  1059.     }
  1060.  
  1061.     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1062.  
  1063.     if (me->io_suspended) {
  1064.         if (_PR_PENDING_INTERRUPT(me)) {
  1065.             me->flags &= ~_PR_INTERRUPT;
  1066.             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1067.         } else {
  1068.             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1069.         }
  1070.         return -1;
  1071.     }
  1072.  
  1073.     if (me->md.blocked_io_status == 0) {
  1074.         _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
  1075.         return -1;
  1076.     }
  1077.  
  1078.     if (!fast)
  1079.         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
  1080.  
  1081.     /* IO is done */
  1082.     GetAcceptExSockaddrs(
  1083.             me->md.acceptex_buf,
  1084.             0,
  1085.             INET_ADDR_PADDED,
  1086.             INET_ADDR_PADDED,
  1087.             (LPSOCKADDR *)&(Laddr),
  1088.             &llen,
  1089.             (LPSOCKADDR *)&(Raddr),
  1090.             (unsigned int *)rlen);
  1091.  
  1092.     if (raddr != NULL)
  1093.         memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
  1094.  
  1095.     PR_ASSERT(me->io_pending == PR_FALSE);
  1096.  
  1097.     return accept_sock;
  1098. }
  1099.  
  1100. PRInt32
  1101. _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, 
  1102.                    void *buf, PRInt32 amount, PRIntervalTime timeout, 
  1103.                    PRBool update, _PR_AcceptTimeoutCallback callback, 
  1104.                    void *callbackArg)
  1105. {
  1106.     PRInt32 sock = sd->secret->md.osfd;
  1107.     PRThread *me = _PR_MD_CURRENT_THREAD();
  1108.     int bytes;
  1109.     PRNetAddr *Laddr;
  1110.     PRUint32 llen, rlen, err;
  1111.     int rv;
  1112.     PRBool isConnected;
  1113.     PRBool madeCallback = PR_FALSE;
  1114.  
  1115.     if (!_nt_use_async) {
  1116.         PRFileDesc *nd;
  1117.         bytes = _PR_EmulateAcceptRead(sd, &nd, raddr, buf, amount, timeout);
  1118.         if (bytes != -1) {
  1119.             /*
  1120.              * This part is the same as SocketClose(nd), except
  1121.              * that we don't close the osfd.
  1122.              */
  1123.             PR_ASSERT(nd->secret->state == _PR_FILEDESC_OPEN);
  1124.             *newSock = nd->secret->md.osfd;
  1125.             nd->secret->state = _PR_FILEDESC_CLOSED;
  1126.             PR_FreeFileDesc(nd);
  1127.         }
  1128.         return bytes;
  1129.     }
  1130.  
  1131.     if (!sd->secret->md.io_model_committed) {
  1132.         rv = _md_Associate((HANDLE)sock);
  1133.         PR_ASSERT(0 != rv);
  1134.         sd->secret->md.io_model_committed = PR_TRUE;
  1135.     }
  1136.  
  1137.     *newSock = _md_get_recycled_socket();
  1138.     if (*newSock == INVALID_SOCKET)
  1139.         return -1;
  1140.  
  1141.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1142.  
  1143.     PR_ASSERT(me->io_suspended == PR_FALSE);
  1144.  
  1145.     me->io_pending = PR_TRUE;
  1146.     me->io_fd = sock;
  1147.     me->state = _PR_IO_WAIT;
  1148.     rv = AcceptEx((SOCKET)sock,
  1149.                   *newSock,
  1150.                   buf,
  1151.                   amount,
  1152.                   INET_ADDR_PADDED,
  1153.                   INET_ADDR_PADDED,
  1154.                   &bytes,
  1155.                   &(me->md.overlapped));
  1156.  
  1157.     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
  1158.         me->io_pending = PR_FALSE;
  1159.         me->state = _PR_RUNNING;
  1160.         _PR_MD_MAP_ACCEPTEX_ERROR(err);
  1161.         return -1;
  1162.     }
  1163.  
  1164.     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
  1165.         PR_ASSERT(0);
  1166.         return -1;
  1167.     }
  1168.  
  1169. retry:
  1170.     if (me->io_suspended) {
  1171.         PRInt32 err;
  1172.         INT seconds;
  1173.         INT bytes = sizeof(seconds);
  1174.  
  1175.         PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
  1176.  
  1177.         err = getsockopt(*newSock, 
  1178.                          SOL_SOCKET,
  1179.                          SO_CONNECT_TIME,
  1180.                          (char *)&seconds,
  1181.                          (PINT)&bytes);
  1182.         if ( err == NO_ERROR ) {
  1183.             PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
  1184.  
  1185.             if (seconds == 0xffffffff) 
  1186.                 isConnected = PR_FALSE;
  1187.             else 
  1188.                 isConnected = PR_TRUE;
  1189.  
  1190.             if (!isConnected) {
  1191.                 if (madeCallback == PR_FALSE && callback)
  1192.                     callback(callbackArg);
  1193.                 madeCallback = PR_TRUE;
  1194.                 me->state = _PR_IO_WAIT;
  1195.                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
  1196.                     return -1;
  1197.                 goto retry;
  1198.             }
  1199.  
  1200.             if (elapsed < timeout) {
  1201.                 /* Socket is not connected but time not elapsed, RESUME IO */
  1202.                 timeout -= elapsed;
  1203.                 me->state = _PR_IO_WAIT;
  1204.                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
  1205.                     return -1;
  1206.                 goto retry;
  1207.             }
  1208.         } else {
  1209.             /*  What to do here? Assume socket not open?*/
  1210.             PR_ASSERT(0);
  1211.             isConnected = PR_FALSE;
  1212.         }
  1213.  
  1214.         rv = _NT_IO_ABORT(*newSock);
  1215.  
  1216.         PR_ASSERT(me->io_suspended ==  PR_FALSE);
  1217.         PR_ASSERT(me->io_pending ==  PR_FALSE);
  1218.         /* If the IO is still suspended, it means we didn't get any 
  1219.          * completion from NT_IO_WAIT.  This is not disasterous, I hope,
  1220.          * but it may mean we still have an IO outstanding...  Try to 
  1221.          * recover by just allowing ourselves to continue.
  1222.          */
  1223.         me->io_suspended = PR_FALSE;
  1224.         if (_PR_PENDING_INTERRUPT(me)) {
  1225.             me->flags &= ~_PR_INTERRUPT;
  1226.             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1227.         } else {
  1228.             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1229.         }
  1230.         me->state = _PR_RUNNING;
  1231.         return -1;
  1232.     }
  1233.  
  1234.     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1235.  
  1236.     if (me->md.blocked_io_status == 0) {
  1237.         _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
  1238.         closesocket(*newSock);
  1239.         return -1;
  1240.     }
  1241.  
  1242.     if (update) 
  1243.         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
  1244.  
  1245.     /* IO is done */
  1246.     GetAcceptExSockaddrs(
  1247.             buf,
  1248.             amount,
  1249.             INET_ADDR_PADDED,
  1250.             INET_ADDR_PADDED,
  1251.             (LPSOCKADDR *)&(Laddr),
  1252.             &llen,
  1253.             (LPSOCKADDR *)(raddr),
  1254.             (unsigned int *)&rlen);
  1255.  
  1256.     PR_ASSERT(me->io_pending == PR_FALSE);
  1257.  
  1258.     return me->md.blocked_io_bytes;
  1259. }
  1260.  
  1261. PRInt32
  1262. _PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen, 
  1263.                     PRInt32 flags, PRIntervalTime timeout)
  1264. {
  1265.     PRThread *me = _PR_MD_CURRENT_THREAD();
  1266.     PRInt32 tflags;
  1267.     int rv, err;
  1268.  
  1269.     if (!_nt_use_async) {
  1270.         if (!sock->secret->md.io_model_committed) {
  1271.             rv = _md_MakeNonblock((HANDLE)sock->secret->md.osfd);
  1272.             PR_ASSERT(0 != rv);
  1273.             sock->secret->md.io_model_committed = PR_TRUE;
  1274.         }
  1275.         return _PR_EmulateTransmitFile(sock, file, headers, hlen, flags, timeout);
  1276.     }
  1277.  
  1278.     if (!sock->secret->md.io_model_committed) {
  1279.         rv = _md_Associate((HANDLE)sock->secret->md.osfd);
  1280.         PR_ASSERT(0 != rv);
  1281.         sock->secret->md.io_model_committed = PR_TRUE;
  1282.     }
  1283.     if (!me->md.xmit_bufs) {
  1284.         me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
  1285.         if (!me->md.xmit_bufs) {
  1286.             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
  1287.             return -1;
  1288.         }
  1289.     }
  1290.     me->md.xmit_bufs->Head       = (void *)headers;
  1291.     me->md.xmit_bufs->HeadLength = hlen;
  1292.     me->md.xmit_bufs->Tail       = (void *)NULL;
  1293.     me->md.xmit_bufs->TailLength = 0;
  1294.  
  1295.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1296.  
  1297.     PR_ASSERT(me->io_suspended == PR_FALSE);
  1298.  
  1299.     tflags = 0;
  1300.     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
  1301.         tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
  1302.  
  1303.     me->io_pending = PR_TRUE;
  1304.     me->io_fd = sock->secret->md.osfd;
  1305.     me->state = _PR_IO_WAIT;
  1306.     rv = TransmitFile((SOCKET)sock->secret->md.osfd,
  1307.                       (HANDLE)file->secret->md.osfd,
  1308.                       (DWORD)0,
  1309.                       (DWORD)0,
  1310.                       (LPOVERLAPPED)&(me->md.overlapped),
  1311.                       (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
  1312.                       (DWORD)tflags);
  1313.     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
  1314.         me->io_pending = PR_FALSE;
  1315.         me->state = _PR_RUNNING;
  1316.         _PR_MD_MAP_TRANSMITFILE_ERROR(err);
  1317.         return -1;
  1318.     }
  1319.  
  1320.     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
  1321.         PR_ASSERT(0);
  1322.         return -1;
  1323.     }
  1324.  
  1325.     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1326.  
  1327.     if (me->io_suspended) {
  1328.         if (_PR_PENDING_INTERRUPT(me)) {
  1329.             me->flags &= ~_PR_INTERRUPT;
  1330.             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1331.         } else {
  1332.             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1333.         }
  1334.         return -1;
  1335.     }
  1336.  
  1337.     if (me->md.blocked_io_status == 0) {
  1338.         _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
  1339.         return -1;
  1340.     }
  1341.  
  1342.     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
  1343.         _md_put_recycled_socket(sock->secret->md.osfd);
  1344.     }
  1345.  
  1346.     PR_ASSERT(me->io_pending == PR_FALSE);
  1347.  
  1348.     return me->md.blocked_io_bytes;
  1349. }
  1350.  
  1351. PRInt32
  1352. _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, 
  1353.             PRIntervalTime timeout)
  1354. {
  1355.     PRInt32 osfd = fd->secret->md.osfd;
  1356.     PRThread *me = _PR_MD_CURRENT_THREAD();
  1357.     int bytes;
  1358.     int rv, err;
  1359.  
  1360.     if (!_nt_use_async || fd->secret->nonblocking) {
  1361.         if (!fd->secret->md.io_model_committed) {
  1362.             rv = _md_MakeNonblock((HANDLE)osfd);
  1363.             PR_ASSERT(0 != rv);
  1364.             fd->secret->md.io_model_committed = PR_TRUE;
  1365.         }
  1366.         return _nt_nonblock_recv(fd, buf, amount, timeout);
  1367.     }
  1368.  
  1369.     if (!fd->secret->md.io_model_committed) {
  1370.         rv = _md_Associate((HANDLE)osfd);
  1371.         PR_ASSERT(0 != rv);
  1372.         fd->secret->md.io_model_committed = PR_TRUE;
  1373.     }
  1374.  
  1375.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1376.  
  1377.     PR_ASSERT(me->io_suspended == PR_FALSE);
  1378.  
  1379.     me->io_pending = PR_TRUE;
  1380.     me->io_fd = osfd;
  1381.     me->state = _PR_IO_WAIT;
  1382.     rv = ReadFile((HANDLE)osfd,
  1383.                   buf, 
  1384.                   amount,
  1385.                   &bytes,
  1386.                   &(me->md.overlapped));
  1387.     if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
  1388.         me->io_pending = PR_FALSE;
  1389.         me->state = _PR_RUNNING;
  1390.         if ((err = GetLastError()) == ERROR_HANDLE_EOF)
  1391.             return 0;
  1392.         _PR_MD_MAP_READ_ERROR(err);
  1393.         return -1;
  1394.     }
  1395.  
  1396.     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
  1397.         PR_ASSERT(0);
  1398.         return -1;
  1399.     }
  1400.  
  1401.     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1402.  
  1403.     if (me->io_suspended) {
  1404.         if (_PR_PENDING_INTERRUPT(me)) {
  1405.             me->flags &= ~_PR_INTERRUPT;
  1406.             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1407.         } else {
  1408.             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1409.         }
  1410.         return -1;
  1411.     }
  1412.  
  1413.     if (me->md.blocked_io_status == 0) {
  1414.         if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
  1415.             return 0;
  1416.         _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
  1417.         return -1;
  1418.     }
  1419.  
  1420.     PR_ASSERT(me->io_pending == PR_FALSE);
  1421.  
  1422.     return me->md.blocked_io_bytes;
  1423. }
  1424.  
  1425. PRInt32
  1426. _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
  1427.             PRIntervalTime timeout)
  1428. {
  1429.     PRInt32 osfd = fd->secret->md.osfd;
  1430.     PRThread *me = _PR_MD_CURRENT_THREAD();
  1431.     int bytes;
  1432.     int rv, err;
  1433.  
  1434.     if (!_nt_use_async || fd->secret->nonblocking) {
  1435.         if (!fd->secret->md.io_model_committed) {
  1436.             rv = _md_MakeNonblock((HANDLE)osfd);
  1437.             PR_ASSERT(0 != rv);
  1438.             fd->secret->md.io_model_committed = PR_TRUE;
  1439.         }
  1440.         return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
  1441.     }
  1442.  
  1443.     if (!fd->secret->md.io_model_committed) {
  1444.         rv = _md_Associate((HANDLE)osfd);
  1445.         PR_ASSERT(0 != rv);
  1446.         fd->secret->md.io_model_committed = PR_TRUE;
  1447.     }
  1448.  
  1449.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1450.  
  1451.     PR_ASSERT(me->io_suspended == PR_FALSE);
  1452.  
  1453.     me->io_pending = PR_TRUE;
  1454.     me->io_fd = osfd;
  1455.     me->state = _PR_IO_WAIT;
  1456.     rv = WriteFile((HANDLE)osfd,
  1457.                    buf, 
  1458.                    amount,
  1459.                    &bytes,
  1460.                    &(me->md.overlapped));
  1461.     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
  1462.         me->io_pending = PR_FALSE;
  1463.         me->state = _PR_RUNNING;
  1464.         _PR_MD_MAP_WRITE_ERROR(err);
  1465.         return -1;
  1466.     }
  1467.  
  1468.     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
  1469.         PR_ASSERT(0);
  1470.         return -1;
  1471.     }
  1472.  
  1473.     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1474.  
  1475.     if (me->io_suspended) {
  1476.         if (_PR_PENDING_INTERRUPT(me)) {
  1477.             me->flags &= ~_PR_INTERRUPT;
  1478.             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1479.         } else {
  1480.             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1481.         }
  1482.         return -1;
  1483.     }
  1484.  
  1485.     if (me->md.blocked_io_status == 0) {
  1486.         _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
  1487.         return -1;
  1488.     }
  1489.  
  1490.     PR_ASSERT(me->io_pending == PR_FALSE);
  1491.  
  1492.     return me->md.blocked_io_bytes;
  1493. }
  1494.  
  1495. PRInt32
  1496. _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
  1497.               const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
  1498. {
  1499.     PRInt32 osfd = fd->secret->md.osfd;
  1500.     PRInt32 rv;
  1501.  
  1502.     if (!fd->secret->md.io_model_committed) {
  1503.         rv = _md_MakeNonblock((HANDLE)osfd);
  1504.         PR_ASSERT(0 != rv);
  1505.         fd->secret->md.io_model_committed = PR_TRUE;
  1506.     }
  1507.     if (_nt_use_async && !fd->secret->nonblocking)
  1508.         return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
  1509.     else
  1510.         return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
  1511. }
  1512.  
  1513. PRInt32
  1514. _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
  1515.                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
  1516. {
  1517.     PRInt32 osfd = fd->secret->md.osfd;
  1518.     PRInt32 rv;
  1519.  
  1520.     if (!fd->secret->md.io_model_committed) {
  1521.         rv = _md_MakeNonblock((HANDLE)osfd);
  1522.         PR_ASSERT(0 != rv);
  1523.         fd->secret->md.io_model_committed = PR_TRUE;
  1524.     }
  1525.     if (_nt_use_async && !fd->secret->nonblocking)
  1526.         return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
  1527.     else
  1528.         return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
  1529. }
  1530.  
  1531. /* XXXMB - for now this is a sockets call only */
  1532. PRInt32
  1533. _PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
  1534. {
  1535.     PRInt32 osfd = fd->secret->md.osfd;
  1536.     int index;
  1537.     int sent = 0;
  1538.     int rv;
  1539.  
  1540.     if (!_nt_use_async || fd->secret->nonblocking) {
  1541.         if (!fd->secret->md.io_model_committed) {
  1542.             rv = _md_MakeNonblock((HANDLE)osfd);
  1543.             PR_ASSERT(0 != rv);
  1544.             fd->secret->md.io_model_committed = PR_TRUE;
  1545.         }
  1546.         return _nt_nonblock_writev(fd, iov, iov_size, timeout);
  1547.     }
  1548.  
  1549.     for (index=0; index<iov_size; index++) {
  1550.         rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
  1551.                         timeout);
  1552.         if (rv > 0) 
  1553.             sent += rv;
  1554.         if ( rv != iov[index].iov_len ) {
  1555.             if (sent <= 0)
  1556.                 return -1;
  1557.             return -1;
  1558.         }
  1559.     }
  1560.  
  1561.     return sent;
  1562. }
  1563.  
  1564. PRInt32
  1565. _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
  1566. {
  1567.     PRInt32 rv;
  1568.  
  1569.     rv = listen(fd->secret->md.osfd, backlog);
  1570.     if (rv < 0)
  1571.         _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
  1572.     return(rv);
  1573. }
  1574.  
  1575. PRInt32
  1576. _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
  1577. {
  1578.     PRInt32 rv;
  1579.  
  1580.     rv = shutdown(fd->secret->md.osfd, how);
  1581.     if (rv < 0)
  1582.         _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
  1583.     return(rv);
  1584. }
  1585.  
  1586. PRStatus
  1587. _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
  1588. {
  1589.     PRInt32 rv;
  1590.  
  1591.     rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
  1592.     if (rv==0)
  1593.         return PR_SUCCESS;
  1594.     else {
  1595.         _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
  1596.         return PR_FAILURE;
  1597.     }
  1598. }
  1599.  
  1600. PRStatus
  1601. _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
  1602. {
  1603.     PRInt32 rv;
  1604.  
  1605.     /*
  1606.      * NT has a bug that, when invoked on a socket accepted by
  1607.      * AcceptEx(), getpeername() returns an all-zero peer address.
  1608.      * To work around this bug, we store the peer's address (returned
  1609.      * by AcceptEx()) with the socket fd and use the cached peer
  1610.      * address if the socket is an accepted socket.
  1611.      */
  1612.  
  1613.     if (fd->secret->md.accepted_socket) {
  1614.         INT seconds;
  1615.         INT bytes = sizeof(seconds);
  1616.  
  1617.         /*
  1618.          * Determine if the socket is connected.
  1619.          */
  1620.  
  1621.         rv = getsockopt(fd->secret->md.osfd, 
  1622.                         SOL_SOCKET,
  1623.                         SO_CONNECT_TIME,
  1624.                         (char *) &seconds,
  1625.                         (PINT) &bytes);
  1626.         if (rv == NO_ERROR) {
  1627.             if (seconds == 0xffffffff) {
  1628.                 PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
  1629.                 return PR_FAILURE;
  1630.             }
  1631.             *len = PR_NETADDR_SIZE(addr);
  1632.             memcpy(addr, &fd->secret->md.peer_addr, *len);
  1633.             return PR_SUCCESS;
  1634.         } else {
  1635.             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
  1636.             return PR_FAILURE;
  1637.         }
  1638.     } else { 
  1639.         rv = getpeername((SOCKET)fd->secret->md.osfd,
  1640.                          (struct sockaddr *) addr, len);
  1641.         if (rv == 0) {
  1642.             return PR_SUCCESS;
  1643.         } else {
  1644.             _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
  1645.             return PR_FAILURE;
  1646.         }
  1647.     }
  1648. }
  1649.  
  1650. PRStatus
  1651. _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
  1652. {
  1653.     PRInt32 rv;
  1654.  
  1655.     rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
  1656.     if (rv==0)
  1657.         return PR_SUCCESS;
  1658.     else {
  1659.         _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
  1660.         return PR_FAILURE;
  1661.     }
  1662. }
  1663.  
  1664. PRStatus
  1665. _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
  1666. {
  1667.     PRInt32 rv;
  1668.  
  1669.     rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
  1670.     if (rv==0)
  1671.         return PR_SUCCESS;
  1672.     else {
  1673.         _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
  1674.         return PR_FAILURE;
  1675.     }
  1676. }
  1677.  
  1678. /* --- FILE IO ----------------------------------------------------------- */
  1679.  
  1680. PRInt32
  1681. _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
  1682. {
  1683.     HANDLE file;
  1684.     PRInt32 access = 0;
  1685.     PRInt32 flags = 0;
  1686.     PRInt32 flag6 = 0;
  1687.     
  1688.     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
  1689.  
  1690.     if (_nt_use_async)
  1691.     {
  1692.         if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
  1693.         if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
  1694.  
  1695.         if (osflags & PR_CREATE_FILE)
  1696.             flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
  1697.         else if (osflags & PR_TRUNCATE) flags = CREATE_ALWAYS;
  1698.         else flags = OPEN_EXISTING;
  1699.         
  1700.         flag6 |= FILE_FLAG_OVERLAPPED;
  1701.  
  1702.         file = CreateFile(name, 
  1703.                           access, 
  1704.                           FILE_SHARE_READ|FILE_SHARE_WRITE,
  1705.                           NULL,
  1706.                           flags, 
  1707.                           flag6,
  1708.                           NULL);
  1709.         if (file == INVALID_HANDLE_VALUE) {
  1710.             _PR_MD_MAP_OPEN_ERROR(GetLastError());
  1711.             return -1;
  1712.         }
  1713.  
  1714.         if (_md_Associate(file) == 0) {
  1715.             CloseHandle(file);
  1716.             return -1;
  1717.         }
  1718.  
  1719.         if (osflags & PR_APPEND) {
  1720.             if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
  1721.                 _PR_MD_MAP_LSEEK_ERROR(GetLastError());
  1722.                 CloseHandle(file);
  1723.                 return -1;
  1724.             }
  1725.         }
  1726.  
  1727.         return (PRInt32)file;
  1728.     }
  1729.     else
  1730.     {
  1731.    
  1732.         if (osflags & PR_RDONLY || osflags & PR_RDWR)
  1733.             access |= GENERIC_READ;
  1734.         if (osflags & PR_WRONLY || osflags & PR_RDWR)
  1735.             access |= GENERIC_WRITE;
  1736.         if (osflags & PR_CREATE_FILE)
  1737.             flags = OPEN_ALWAYS;
  1738.         else if (osflags & PR_TRUNCATE)
  1739.             flags = CREATE_ALWAYS;
  1740.         else
  1741.             flags = OPEN_EXISTING;
  1742.  
  1743.         file = CreateFile(name,
  1744.                           access,
  1745.                           FILE_SHARE_READ|FILE_SHARE_WRITE,
  1746.                           NULL,
  1747.                           flags,
  1748.                           flag6,
  1749.                           NULL);
  1750.         if (file == INVALID_HANDLE_VALUE) {
  1751.             _PR_MD_MAP_OPEN_ERROR(GetLastError());
  1752.             return -1; 
  1753.         }
  1754.  
  1755.         /* Note: we didn't bother putting it in nonblocking mode */
  1756.         return (PRInt32)file;
  1757.     }
  1758. }
  1759.  
  1760. PRInt32 
  1761. _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
  1762. {
  1763.     PRInt32 f = fd->secret->md.osfd;
  1764.     PRUint32 bytes;
  1765.     int rv, err;
  1766.  
  1767.     if (_nt_use_async && !fd->secret->md.nonoverlapped) {
  1768.         PRThread *me = _PR_MD_CURRENT_THREAD();
  1769.  
  1770.         memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1771.  
  1772.         PR_ASSERT(me->io_suspended == PR_FALSE);
  1773.  
  1774.         me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
  1775.  
  1776.         me->io_pending = PR_TRUE;
  1777.         me->io_fd = f;
  1778.         me->state = _PR_IO_WAIT;
  1779.         rv = ReadFile((HANDLE)f, 
  1780.                       (LPVOID)buf, 
  1781.                       len, 
  1782.                       &bytes, 
  1783.                       &me->md.overlapped);
  1784.         if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
  1785.             me->io_pending = PR_FALSE;
  1786.             me->state = _PR_RUNNING;
  1787.             if (err == ERROR_HANDLE_EOF)
  1788.                 return 0;
  1789.             _PR_MD_MAP_READ_ERROR(err);
  1790.             return -1;
  1791.         }
  1792.  
  1793.         if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
  1794.             PR_ASSERT(0);
  1795.             return -1;
  1796.         }
  1797.  
  1798.         PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1799.  
  1800.         if (me->io_suspended) {
  1801.             if (_PR_PENDING_INTERRUPT(me)) {
  1802.                 me->flags &= ~_PR_INTERRUPT;
  1803.                 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1804.             } else {
  1805.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1806.             }
  1807.             return -1;
  1808.         }
  1809.  
  1810.         if (me->md.blocked_io_status == 0) {
  1811.             if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
  1812.                 return 0;
  1813.             _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
  1814.             return -1;
  1815.         }
  1816.  
  1817.         SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
  1818.     
  1819.         PR_ASSERT(me->io_pending == PR_FALSE);
  1820.  
  1821.         return me->md.blocked_io_bytes;
  1822.     } else {
  1823.  
  1824.         rv = ReadFile((HANDLE)f,
  1825.                       (LPVOID)buf,
  1826.                       len,
  1827.                       &bytes,
  1828.                       NULL);
  1829.         if (rv == 0) {
  1830.             err = GetLastError();
  1831.             /* ERROR_HANDLE_EOF can only be returned by async io */
  1832.             PR_ASSERT(err != ERROR_HANDLE_EOF);
  1833.             if (err == ERROR_BROKEN_PIPE) {
  1834.                 /* The write end of the pipe has been closed. */ 
  1835.                 return 0;
  1836.             }
  1837.             _PR_MD_MAP_READ_ERROR(err);
  1838.             return -1;
  1839.         }
  1840.         return bytes;
  1841.     }
  1842. }
  1843.  
  1844. PRInt32
  1845. _PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len)
  1846. {
  1847.     PRInt32 f = fd->secret->md.osfd;
  1848.     PRInt32 bytes;
  1849.     int rv, err;
  1850.     PRThread *me = _PR_MD_CURRENT_THREAD();
  1851.  
  1852.     if (_nt_use_async && !fd->secret->md.nonoverlapped) {
  1853.         memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  1854.  
  1855.         PR_ASSERT(me->io_suspended == PR_FALSE);
  1856.  
  1857.         me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
  1858.  
  1859.         me->io_pending = PR_TRUE;
  1860.         me->io_fd = f;
  1861.         me->state = _PR_IO_WAIT;
  1862.         rv = WriteFile((HANDLE)f, 
  1863.                        buf, 
  1864.                        len, 
  1865.                        &bytes, 
  1866.                        &(me->md.overlapped));
  1867.         if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
  1868.             me->io_pending = PR_FALSE;
  1869.             me->state = _PR_RUNNING;
  1870.             _PR_MD_MAP_WRITE_ERROR(err);
  1871.             return -1;
  1872.         }
  1873.  
  1874.         if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
  1875.             PR_ASSERT(0);
  1876.             return -1;
  1877.         }
  1878.  
  1879.         PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
  1880.  
  1881.         if (me->io_suspended) {
  1882.             if (_PR_PENDING_INTERRUPT(me)) {
  1883.                 me->flags &= ~_PR_INTERRUPT;
  1884.                 PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
  1885.             } else {
  1886.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  1887.             }
  1888.             return -1;
  1889.         }
  1890.  
  1891.         if (me->md.blocked_io_status == 0) {
  1892.             _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
  1893.             return -1;
  1894.         }
  1895.  
  1896.         SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
  1897.     
  1898.         PR_ASSERT(me->io_pending == PR_FALSE);
  1899.  
  1900.         return me->md.blocked_io_bytes;
  1901.     } else {
  1902.         rv = WriteFile((HANDLE)f,
  1903.                        buf,
  1904.                        len,
  1905.                        &bytes,
  1906.                        NULL);
  1907.         if (rv == 0) {
  1908.             _PR_MD_MAP_WRITE_ERROR(GetLastError());
  1909.             return -1;
  1910.         }
  1911.         return bytes;
  1912.     }
  1913. }
  1914.  
  1915. PRInt32
  1916. _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
  1917. {
  1918.     PRInt32 result;
  1919.  
  1920.     if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
  1921.         PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
  1922.         return -1;
  1923.     }
  1924.     return result;
  1925. }
  1926.  
  1927. PRInt32
  1928. _PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
  1929. {
  1930.     PRInt32 rv;
  1931.  
  1932.     rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, whence);
  1933.     /*
  1934.      * If the lpDistanceToMoveHigh argument (third argument) is
  1935.      * NULL, SetFilePointer returns 0xffffffff on failure.
  1936.      */
  1937.     if (-1 == rv) {
  1938.         _PR_MD_MAP_LSEEK_ERROR(GetLastError());
  1939.     }
  1940.     return rv;
  1941. }
  1942.  
  1943. PRInt64
  1944. _PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
  1945. {
  1946.     PRInt64 result;
  1947.     PRInt32 rv, low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
  1948.  
  1949.     rv = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
  1950.  
  1951.     /*
  1952.      * If the lpDistanceToMoveHigh argument (third argument) is
  1953.      * NULL, SetFilePointer returns 0xffffffff on failure.
  1954.      */
  1955.     if (-1 == rv)
  1956.     {
  1957.         _PR_MD_MAP_LSEEK_ERROR(GetLastError());
  1958.         return -1;
  1959.     }
  1960.  
  1961.     result = (hi << 32) + rv;
  1962.     return result;
  1963. }
  1964.  
  1965. /*
  1966.  * This is documented to succeed on read-only files, but Win32's
  1967.  * FlushFileBuffers functions fails with "access denied" in such a
  1968.  * case.  So we only signal an error if the error is *not* "access
  1969.  * denied".
  1970.  */
  1971. PRInt32
  1972. _PR_MD_FSYNC(PRFileDesc *fd)
  1973. {
  1974.     /*
  1975.      * From the documentation:
  1976.      *
  1977.      *       On Windows NT, the function FlushFileBuffers fails if hFile
  1978.      *       is a handle to console output. That is because console
  1979.      *       output is not buffered. The function returns FALSE, and
  1980.      *       GetLastError returns ERROR_INVALID_HANDLE.
  1981.      *
  1982.      * On the other hand, on Win95, it returns without error.  I cannot
  1983.      * assume that 0, 1, and 2 are console, because if someone closes
  1984.      * System.out and then opens a file, they might get file descriptor
  1985.      * 1.  An error on *that* version of 1 should be reported, whereas
  1986.      * an error on System.out (which was the original 1) should be
  1987.      * ignored.  So I use isatty() to ensure that such an error was
  1988.      * because of this, and if it was, I ignore the error.
  1989.      */
  1990.  
  1991.     long handle = _get_osfhandle(fd->secret->md.osfd);
  1992.     BOOL ok = FlushFileBuffers((HANDLE)handle);
  1993.  
  1994.     if (!ok) {
  1995.     DWORD err = GetLastError();
  1996.  
  1997.     if (err != ERROR_ACCESS_DENIED) {    /* from winerror.h */
  1998.             _PR_MD_MAP_FSYNC_ERROR(err);
  1999.         return -1;
  2000.     }
  2001.     }
  2002.     return 0;
  2003. }
  2004.  
  2005. PRInt32
  2006. _PR_MD_CLOSE(PRInt32 osfd, PRBool socket)
  2007. {
  2008.     PRInt32 rv;
  2009.     PRInt32 err;
  2010.     if (_nt_use_async) {
  2011.         PRThread *me = _PR_MD_CURRENT_THREAD();
  2012.  
  2013.         if (socket)  {
  2014.             rv = closesocket((SOCKET)osfd);
  2015.             if (rv < 0)
  2016.                 err = WSAGetLastError();
  2017.         } else {
  2018.             rv = CloseHandle((HANDLE)osfd)?0:-1;
  2019.             if (rv < 0)
  2020.                 err = GetLastError();
  2021.         }
  2022.  
  2023.         if (rv == 0 && me->io_pending) {
  2024.             if (me->io_fd == osfd) {
  2025.                 PRBool fWait;
  2026.  
  2027.                 PR_ASSERT(me->io_suspended == PR_TRUE);
  2028.                 _PR_THREAD_LOCK(me);
  2029.                 me->state = _PR_IO_WAIT;
  2030.                 /* The IO could have completed on another thread just after
  2031.                  * calling closesocket while the io_suspended flag was true.  
  2032.                  * So we now grab the lock to do a safe check on io_pending to
  2033.                  * see if we need to wait or not.  At this point we can check
  2034.                  * io_pending safely because we've reset io_suspended to FALSE.  
  2035.                  * XXXMB - 1-15-97 this seems fishy and begging for a race...
  2036.                  */
  2037.                 fWait = me->io_pending;
  2038.                 me->io_suspended = PR_FALSE;
  2039.                 _PR_THREAD_UNLOCK(me);
  2040.  
  2041.                 if (fWait)
  2042.                     _NT_IO_WAIT(me, CLOSE_TIMEOUT);
  2043.                 PR_ASSERT(me->io_suspended ==  PR_FALSE);
  2044.                 PR_ASSERT(me->io_pending ==  PR_FALSE);
  2045.                 me->io_suspended = PR_FALSE;
  2046.                 me->io_pending = PR_FALSE;
  2047.                 me->state = _PR_RUNNING;
  2048.             }
  2049.             } else {
  2050.                 me->io_suspended = PR_FALSE;
  2051.             if (rv < 0)
  2052.                 _PR_MD_MAP_CLOSE_ERROR(err);
  2053.         }
  2054.         return rv;
  2055.     } else { 
  2056.         if (socket) {
  2057.             rv = closesocket((SOCKET)osfd);
  2058.             if (rv == -1)
  2059.                 _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
  2060.         } else {
  2061.             rv = CloseHandle((HANDLE)osfd)?0:-1;
  2062.             if (rv == -1)
  2063.                 _PR_MD_MAP_CLOSE_ERROR(GetLastError());
  2064.         }
  2065.     }
  2066. }
  2067.  
  2068.  
  2069. /* --- DIR IO ------------------------------------------------------------ */
  2070. #define GetFileFromDIR(d)       (d)->d_entry.cFileName
  2071. #define FileIsHidden(d)       ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
  2072.  
  2073. void FlipSlashes(char *cp, int len)
  2074. {
  2075.     while (--len >= 0) {
  2076.     if (cp[0] == '/') {
  2077.         cp[0] = PR_DIRECTORY_SEPARATOR;
  2078.     }
  2079.     cp++;
  2080.     }
  2081. }
  2082.  
  2083. /*
  2084. **
  2085. ** Local implementations of standard Unix RTL functions which are not provided
  2086. ** by the VC RTL.
  2087. **
  2088. */
  2089.  
  2090. PRStatus
  2091. _PR_MD_CLOSE_DIR(_MDDir *d)
  2092. {
  2093.     if ( d ) {
  2094.         if (FindClose( d->d_hdl )) {
  2095.             d->magic = (PRUint32)-1;
  2096.             return PR_SUCCESS;
  2097.         } else {
  2098.             _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
  2099.             return PR_FAILURE;
  2100.         }
  2101.     }
  2102.     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  2103.     return PR_FAILURE;
  2104. }
  2105.  
  2106.  
  2107. PRStatus
  2108. _PR_MD_OPEN_DIR(_MDDir *d, const char *name)
  2109. {
  2110.     char filename[ MAX_PATH ];
  2111.  
  2112.     PR_snprintf(filename, MAX_PATH, "%s%s%s",
  2113.                 name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
  2114.     FlipSlashes( filename, strlen(filename) );
  2115.  
  2116.     d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
  2117.     if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
  2118.         _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
  2119.         return PR_FAILURE;
  2120.     }
  2121.     d->firstEntry = PR_TRUE;
  2122.     d->magic = _MD_MAGIC_DIR;
  2123.     return PR_SUCCESS;
  2124. }
  2125.  
  2126. char *
  2127. _PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
  2128. {
  2129.     PRInt32 err;
  2130.     BOOL rv;
  2131.     char *fileName;
  2132.  
  2133.     if ( d ) {
  2134.         while (1) {
  2135.             if (d->firstEntry) {
  2136.                 d->firstEntry = PR_FALSE;
  2137.                 rv = 1;
  2138.             } else {
  2139.                 rv = FindNextFile(d->d_hdl, &(d->d_entry));
  2140.             }
  2141.             if (rv == 0) {
  2142.                 break;
  2143.             }
  2144.             fileName = GetFileFromDIR(d);
  2145.             if ( (flags & PR_SKIP_DOT) &&
  2146.                  (fileName[0] == '.') && (fileName[1] == '\0'))
  2147.                  continue;
  2148.             if ( (flags & PR_SKIP_DOT_DOT) &&
  2149.                  (fileName[0] == '.') && (fileName[1] == '.') &&
  2150.                  (fileName[2] == '\0'))
  2151.                  continue;
  2152.             if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
  2153.                  continue;
  2154.             return fileName;
  2155.         }
  2156.     err = GetLastError();
  2157.     PR_ASSERT(NO_ERROR != err);
  2158.             _PR_MD_MAP_READDIR_ERROR(err);
  2159.     return NULL;
  2160.         }
  2161.     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  2162.     return NULL;
  2163. }
  2164.  
  2165. PRInt32
  2166. _PR_MD_DELETE(const char *name)
  2167. {
  2168.     if (DeleteFile(name)) {
  2169.         return 0;
  2170.     } else {
  2171.         _PR_MD_MAP_DELETE_ERROR(GetLastError());
  2172.         return -1;
  2173.     }
  2174. }
  2175.  
  2176. static void
  2177. _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
  2178. {
  2179.     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
  2180.     CopyMemory(prtm, filetime, sizeof(PRTime));
  2181.     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
  2182.  
  2183. #ifdef DEBUG
  2184.     /* Doublecheck our calculation. */
  2185.     {
  2186.         SYSTEMTIME systime;
  2187.         PRExplodedTime etm;
  2188.         PRTime cmp; /* for comparison */
  2189.         BOOL rv;
  2190.  
  2191.         rv = FileTimeToSystemTime(filetime, &systime);
  2192.         PR_ASSERT(0 != rv);
  2193.  
  2194.         /*
  2195.          * PR_ImplodeTime ignores wday and yday.
  2196.          */
  2197.         etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
  2198.         etm.tm_sec = systime.wSecond;
  2199.         etm.tm_min = systime.wMinute;
  2200.         etm.tm_hour = systime.wHour;
  2201.         etm.tm_mday = systime.wDay;
  2202.         etm.tm_month = systime.wMonth - 1;
  2203.         etm.tm_year = systime.wYear;
  2204.         /*
  2205.          * It is not well-documented what time zone the FILETIME's
  2206.          * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
  2207.          * But BY_HANDLE_FILE_INFORMATION is unclear about this.
  2208.          * By our best judgement, we assume that FILETIME is in UTC.
  2209.          */
  2210.         etm.tm_params.tp_gmt_offset = 0;
  2211.         etm.tm_params.tp_dst_offset = 0;
  2212.         cmp = PR_ImplodeTime(&etm);
  2213.  
  2214.         /*
  2215.          * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
  2216.          * microseconds to milliseconds before doing the comparison.
  2217.          */
  2218.         PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
  2219.     }
  2220. #endif /* DEBUG */
  2221. }
  2222.  
  2223. PRInt32
  2224. _PR_MD_STAT(const char *fn, struct stat *info)
  2225. {
  2226.     PRInt32 rv;
  2227.  
  2228.     rv = _stat(fn, (struct _stat *)info);
  2229.     if (-1 == rv) {
  2230.         /*
  2231.          * Check for MSVC runtime library _stat() bug.
  2232.          * (It's really a bug in FindFirstFile().)
  2233.          * If a pathname ends in a backslash or slash,
  2234.          * e.g., c:\temp\ or c:/temp/, _stat() will fail.
  2235.          * Note: a pathname ending in a slash (e.g., c:/temp/)
  2236.          * can be handled by _stat() on NT but not on Win95.
  2237.          *
  2238.          * We remove the backslash or slash at the end and
  2239.          * try again.
  2240.          */
  2241.  
  2242.         int len = strlen(fn);
  2243.         if (len > 0 && len <= _MAX_PATH
  2244.                 && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
  2245.             char newfn[_MAX_PATH + 1];
  2246.  
  2247.             strcpy(newfn, fn);
  2248.             newfn[len - 1] = '\0';
  2249.             rv = _stat(newfn, (struct _stat *)info);
  2250.         }
  2251.     }
  2252.  
  2253.     if (-1 == rv) {
  2254.         _PR_MD_MAP_STAT_ERROR(errno);
  2255.     }
  2256.     return rv;
  2257. }
  2258.  
  2259. #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
  2260.  
  2261. /*
  2262.  * IsRootDirectory --
  2263.  *
  2264.  * Return PR_TRUE if the pathname 'fn' is a valid root directory,
  2265.  * else return PR_FALSE.  The char buffer pointed to by 'fn' must
  2266.  * be writable.  During the execution of this function, the contents
  2267.  * of the buffer pointed to by 'fn' may be modified, but on return
  2268.  * the original contents will be restored.  'buflen' is the size of
  2269.  * the buffer pointed to by 'fn'.
  2270.  *
  2271.  * Root directories come in three formats:
  2272.  * 1. / or \, meaning the root directory of the current drive.
  2273.  * 2. C:/ or C:\, where C is a drive letter.
  2274.  * 3. \\<server name>\<share point name>\ or
  2275.  *    \\<server name>\<share point name>, meaning the root directory
  2276.  *    of a UNC (Universal Naming Convention) name.
  2277.  */
  2278.  
  2279. static PRBool
  2280. IsRootDirectory(char *fn, size_t buflen)
  2281. {
  2282.     char *p;
  2283.     PRBool slashAdded = PR_FALSE;
  2284.     PRBool rv = PR_FALSE;
  2285.  
  2286.     if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
  2287.         return PR_TRUE;
  2288.     }
  2289.  
  2290.     if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
  2291.             && fn[3] == '\0') {
  2292.         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
  2293.         return rv;
  2294.     }
  2295.  
  2296.     /* The UNC root directory */
  2297.  
  2298.     if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
  2299.         /* The 'server' part should have at least one character. */
  2300.         p = &fn[2];
  2301.         if (*p == '\0' || _PR_IS_SLASH(*p)) {
  2302.             return PR_FALSE;
  2303.         }
  2304.  
  2305.         /* look for the next slash */
  2306.         do {
  2307.             p++;
  2308.         } while (*p != '\0' && !_PR_IS_SLASH(*p));
  2309.         if (*p == '\0') {
  2310.             return PR_FALSE;
  2311.         }
  2312.  
  2313.         /* The 'share' part should have at least one character. */
  2314.         p++;
  2315.         if (*p == '\0' || _PR_IS_SLASH(*p)) {
  2316.             return PR_FALSE;
  2317.         }
  2318.  
  2319.         /* look for the final slash */
  2320.         do {
  2321.             p++;
  2322.         } while (*p != '\0' && !_PR_IS_SLASH(*p));
  2323.         if (_PR_IS_SLASH(*p) && p[1] != '\0') {
  2324.             return PR_FALSE;
  2325.         }
  2326.         if (*p == '\0') {
  2327.             /*
  2328.              * GetDriveType() doesn't work correctly if the
  2329.              * path is of the form \\server\share, so we add
  2330.              * a final slash temporarily.
  2331.              */
  2332.             if ((p + 1) < (fn + buflen)) {
  2333.                 *p++ = '\\';
  2334.                 *p = '\0';
  2335.                 slashAdded = PR_TRUE;
  2336.             } else {
  2337.                 return PR_FALSE; /* name too long */
  2338.             }
  2339.         }
  2340.         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
  2341.         /* restore the 'fn' buffer */
  2342.         if (slashAdded) {
  2343.             *--p = '\0';
  2344.         }
  2345.     }
  2346.     return rv;
  2347. }
  2348.  
  2349. PRInt32
  2350. _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
  2351. {
  2352.     HANDLE hFindFile;
  2353.     WIN32_FIND_DATA findFileData;
  2354.     char pathbuf[MAX_PATH + 1];
  2355.     
  2356.     if (NULL == fn || '\0' == *fn) {
  2357.         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  2358.         return -1;
  2359.     }
  2360.  
  2361.     /*
  2362.      * FindFirstFile() expands wildcard characters.  So
  2363.      * we make sure the pathname contains no wildcard.
  2364.      */
  2365.     if (NULL != strpbrk(fn, "?*")) {
  2366.         PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
  2367.         return -1;
  2368.     }
  2369.  
  2370.     hFindFile = FindFirstFile(fn, &findFileData);
  2371.     if (INVALID_HANDLE_VALUE == hFindFile) {
  2372.         DWORD len;
  2373.         char *filePart;
  2374.  
  2375.         /*
  2376.          * FindFirstFile() does not work correctly on root directories.
  2377.          * It also doesn't work correctly on a pathname that ends in a
  2378.          * slash.  So we first check to see if the pathname specifies a
  2379.          * root directory.  If not, and if the pathname ends in a slash,
  2380.          * we remove the final slash and try again.
  2381.          */
  2382.  
  2383.         /*
  2384.          * If the pathname does not contain ., \, and /, it cannot be
  2385.          * a root directory or a pathname that ends in a slash.
  2386.          */
  2387.         if (NULL == strpbrk(fn, ".\\/")) {
  2388.             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
  2389.             return -1;
  2390.         } 
  2391.         len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
  2392.                 &filePart);
  2393.         PR_ASSERT(0 != len);
  2394.         if (len > sizeof(pathbuf)) {
  2395.             PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
  2396.             return -1;
  2397.         }
  2398.         if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
  2399.             info->type = PR_FILE_DIRECTORY;
  2400.             info->size = 0;
  2401.             /*
  2402.              * These timestamps don't make sense for root directories.
  2403.              */
  2404.             info->modifyTime = 0;
  2405.             info->creationTime = 0;
  2406.             return 0;
  2407.         }
  2408.         if (!_PR_IS_SLASH(pathbuf[len - 1])) {
  2409.             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
  2410.             return -1;
  2411.         } else {
  2412.             pathbuf[len - 1] = '\0';
  2413.             hFindFile = FindFirstFile(pathbuf, &findFileData);
  2414.             if (INVALID_HANDLE_VALUE == hFindFile) {
  2415.                 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
  2416.                 return -1;
  2417.             }
  2418.         }
  2419.     }
  2420.  
  2421.     FindClose(hFindFile);
  2422.  
  2423.     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  2424.         info->type = PR_FILE_DIRECTORY;
  2425.     } else {
  2426.         info->type = PR_FILE_FILE;
  2427.     }
  2428.  
  2429.     info->size = findFileData.nFileSizeHigh;
  2430.     info->size = (info->size << 32) + findFileData.nFileSizeLow;
  2431.  
  2432.     _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
  2433.  
  2434.     if (0 == findFileData.ftCreationTime.dwLowDateTime &&
  2435.             0 == findFileData.ftCreationTime.dwHighDateTime) {
  2436.         info->creationTime = info->modifyTime;
  2437.     } else {
  2438.         _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
  2439.                 &info->creationTime);
  2440.     }
  2441.  
  2442.     return 0;
  2443. }
  2444.  
  2445. PRInt32
  2446. _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
  2447. {
  2448.     PRFileInfo64 info64;
  2449.     PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
  2450.     if (0 == rv)
  2451.     {
  2452.         info->type = info64.type;
  2453.         info->size = (PRUint32) info64.size;
  2454.         info->modifyTime = info64.modifyTime;
  2455.         info->creationTime = info64.creationTime;
  2456.     }
  2457.     return rv;
  2458. }
  2459.  
  2460. PRInt32
  2461. _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
  2462. {
  2463.     int rv;
  2464.  
  2465.     BY_HANDLE_FILE_INFORMATION hinfo;
  2466.  
  2467.     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
  2468.     if (rv == FALSE) {
  2469.         _PR_MD_MAP_FSTAT_ERROR(GetLastError());
  2470.         return -1;
  2471.     }
  2472.  
  2473.     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  2474.         info->type = PR_FILE_DIRECTORY;
  2475.     else
  2476.         info->type = PR_FILE_FILE;
  2477.  
  2478.     info->size = hinfo.nFileSizeHigh;
  2479.     info->size = (info->size << 32) + hinfo.nFileSizeLow;
  2480.  
  2481.     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
  2482.     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
  2483.  
  2484.     return 0;
  2485. }
  2486.  
  2487. PRInt32
  2488. _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
  2489. {
  2490.     int rv;
  2491.  
  2492.     BY_HANDLE_FILE_INFORMATION hinfo;
  2493.  
  2494.     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
  2495.     if (rv == FALSE) {
  2496.         _PR_MD_MAP_FSTAT_ERROR(GetLastError());
  2497.         return -1;
  2498.     }
  2499.  
  2500.     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  2501.         info->type = PR_FILE_DIRECTORY;
  2502.     else
  2503.         info->type = PR_FILE_FILE;
  2504.  
  2505.     info->size = hinfo.nFileSizeLow;
  2506.  
  2507.     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
  2508.     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
  2509.  
  2510.     return 0;
  2511. }
  2512.  
  2513. PRInt32
  2514. _PR_MD_RENAME(const char *from, const char *to)
  2515. {
  2516.     /* Does this work with dot-relative pathnames? */
  2517.     if (MoveFile(from, to)) {
  2518.         return 0;
  2519.     } else {
  2520.         _PR_MD_MAP_RENAME_ERROR(GetLastError());
  2521.         return -1;
  2522.     }
  2523. }
  2524.  
  2525. PRInt32
  2526. _PR_MD_ACCESS(const char *name, PRIntn how)
  2527. {
  2528.     PRInt32 rv;
  2529.  
  2530.     switch (how) {
  2531.       case PR_ACCESS_WRITE_OK:
  2532.         rv = _access(name, 02);
  2533.         break;
  2534.       case PR_ACCESS_READ_OK:
  2535.         rv = _access(name, 04);
  2536.         break;
  2537.       case PR_ACCESS_EXISTS:
  2538.         rv = _access(name, 00);
  2539.         break;
  2540.       default:
  2541.         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
  2542.         return -1;
  2543.     }
  2544.     if (rv < 0) {
  2545.         _PR_MD_MAP_ACCESS_ERROR(errno);
  2546.     }
  2547.     return rv;
  2548. }
  2549.  
  2550. PRInt32
  2551. _PR_MD_MKDIR(const char *name, PRIntn mode)
  2552. {
  2553.     /* XXXMB - how to translate the "mode"??? */
  2554.     if (CreateDirectory(name, NULL)) {
  2555.         return 0;
  2556.     } else {
  2557.         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
  2558.         return -1;
  2559.     }
  2560. }
  2561.  
  2562. PRInt32
  2563. _PR_MD_RMDIR(const char *name)
  2564. {
  2565.     if (RemoveDirectory(name)) {
  2566.         return 0;
  2567.     } else {
  2568.         _PR_MD_MAP_RMDIR_ERROR(GetLastError());
  2569.         return -1;
  2570.     }
  2571. }
  2572.  
  2573. PRStatus
  2574. _PR_MD_LOCKFILE(PRInt32 f)
  2575. {
  2576.     PRInt32 rv, err;
  2577.     PRThread *me = _PR_MD_CURRENT_THREAD();
  2578.  
  2579.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  2580.  
  2581.     PR_ASSERT(me->io_suspended == PR_FALSE);
  2582.  
  2583.     me->state = _PR_IO_WAIT;
  2584.     me->io_pending = PR_TRUE;
  2585.     rv = LockFileEx((HANDLE)f, 
  2586.                     LOCKFILE_EXCLUSIVE_LOCK,
  2587.                     0,
  2588.                     0x7fffffff,
  2589.                     0,
  2590.                     &me->md.overlapped);
  2591.  
  2592.     /* HACK AROUND NT BUG
  2593.      * NT 3.51 has a bug.  In NT 3.51, if LockFileEx returns true, you
  2594.      * don't get any completion on the completion port.  This is a bug.
  2595.      *
  2596.      * They fixed it on NT4.0 so that you do get a completion.
  2597.      *
  2598.      * If we pretend we won't get a completion, NSPR gets confused later
  2599.      * when the unexpected completion arrives.  If we assume we do get
  2600.      * a completion, we hang on 3.51.  Worse, Microsoft informs me that the 
  2601.      * behavior varies on 3.51 depending on if you are using a network
  2602.      * file system or a local disk!
  2603.      *
  2604.      * Solution:  For now, _nt_version_gets_lockfile_completion is set
  2605.      * depending on whether or not this system is EITHER
  2606.      *      - running NT 4.0
  2607.      *      - running NT 3.51 with a service pack greater than 5.
  2608.      * 
  2609.      * In the meantime, this code may not work on network file systems.
  2610.      *
  2611.      */
  2612.  
  2613.     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
  2614.         me->io_pending = PR_FALSE;
  2615.         me->state = _PR_RUNNING;
  2616.         _PR_MD_MAP_LOCKF_ERROR(err);
  2617.         return PR_FAILURE;
  2618.     }
  2619. #ifdef _NEED_351_FILE_LOCKING_HACK
  2620.     else if (rv)  {
  2621.         /* If this is NT 3.51 and the file is local, then we won't get a 
  2622.          * completion back from LockFile when it succeeded.
  2623.          */
  2624.         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
  2625.             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
  2626.                 me->io_pending = PR_FALSE;
  2627.                 me->state = _PR_RUNNING;
  2628.                 return PR_SUCCESS; 
  2629.             }
  2630.         }
  2631.     }
  2632. #endif /* _NEED_351_FILE_LOCKING_HACK */
  2633.  
  2634.     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
  2635.         me->io_pending = PR_FALSE;
  2636.         me->state = _PR_RUNNING;
  2637.         return PR_FAILURE;
  2638.     }
  2639.  
  2640.     if (me->md.blocked_io_status == 0) {
  2641.         _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
  2642.         return PR_FAILURE;
  2643.     }
  2644.  
  2645.     return PR_SUCCESS;
  2646. }
  2647.  
  2648. PRStatus
  2649. _PR_MD_TLOCKFILE(PRInt32 f)
  2650. {
  2651.     PRInt32 rv, err;
  2652.     PRThread *me = _PR_MD_CURRENT_THREAD();
  2653.  
  2654.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  2655.  
  2656.     PR_ASSERT(me->io_suspended == PR_FALSE);
  2657.  
  2658.     me->state = _PR_IO_WAIT;
  2659.     me->io_pending = PR_TRUE;
  2660.     rv = LockFileEx((HANDLE)f, 
  2661.                     LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
  2662.                     0,
  2663.                     0x7fffffff,
  2664.                     0,
  2665.                     &me->md.overlapped);
  2666.     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
  2667.         me->io_pending = PR_FALSE;
  2668.         me->state = _PR_RUNNING;
  2669.         _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
  2670.         return PR_FAILURE;
  2671.     }
  2672. #ifdef _NEED_351_FILE_LOCKING_HACK
  2673.     else if (rv)  {
  2674.         /* If this is NT 3.51 and the file is local, then we won't get a 
  2675.          * completion back from LockFile when it succeeded.
  2676.          */
  2677.         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
  2678.             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
  2679.                 me->io_pending = PR_FALSE;
  2680.                 me->state = _PR_RUNNING;
  2681.                 return PR_SUCCESS; 
  2682.             }
  2683.         }
  2684.     }
  2685. #endif /* _NEED_351_FILE_LOCKING_HACK */
  2686.  
  2687.     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
  2688.         me->io_pending = PR_FALSE;
  2689.         me->state = _PR_RUNNING;
  2690.         return PR_FAILURE;
  2691.     }
  2692.  
  2693.     if (me->md.blocked_io_status == 0) {
  2694.         _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
  2695.         return PR_FAILURE;
  2696.     }
  2697.  
  2698.     return PR_SUCCESS;
  2699. }
  2700.  
  2701.  
  2702. PRStatus
  2703. _PR_MD_UNLOCKFILE(PRInt32 f)
  2704. {
  2705.     PRInt32 rv;
  2706.     PRThread *me = _PR_MD_CURRENT_THREAD();
  2707.  
  2708.     memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
  2709.  
  2710.     PR_ASSERT(me->io_suspended == PR_FALSE);
  2711.  
  2712.     rv = UnlockFileEx((HANDLE)f,
  2713.                       0,
  2714.                       0x7fffffff,
  2715.                       0,
  2716.                       &me->md.overlapped);
  2717.  
  2718.     if (rv)
  2719.         return PR_SUCCESS;
  2720.     else {
  2721.         int err = GetLastError();
  2722.         _PR_MD_MAP_LOCKF_ERROR(err);
  2723.         return PR_FAILURE;
  2724.     }
  2725. }
  2726.  
  2727. void
  2728. _PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
  2729. {
  2730.     /*
  2731.      * On NT, we either call _md_Associate() or _md_MakeNonblock(),
  2732.      * depending on whether the socket is blocking or not.
  2733.      *
  2734.      * Once we associate a socket with the io completion port,
  2735.      * there is no way to disassociate it from the io completion
  2736.      * port.  So we have to call _md_Associate/_md_MakeNonblock
  2737.      * lazily.
  2738.      */
  2739. }
  2740.  
  2741. #ifdef _NEED_351_FILE_LOCKING_HACK
  2742. /***************
  2743. ** 
  2744. ** Lockfile hacks
  2745. **
  2746. ** The following code is a hack to work around a microsoft bug with lockfile.
  2747. ** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
  2748. ** get a completion back for files that are on local disks.  So, we need to
  2749. ** know if a file is local or remote so we can tell if we should expect 
  2750. ** a completion.
  2751. **
  2752. ** The only way to check if a file is local or remote based on the handle is
  2753. ** to get the serial number for the volume it is mounted on and then to 
  2754. ** compare that with mounted drives.  This code caches the volume numbers of
  2755. ** fixed disks and does a relatively quick check.
  2756. **
  2757. ** Locking:  Since the only thing we ever do when multithreaded is a 32bit
  2758. **           assignment, we probably don't need locking.  It is included just
  2759. **           case anyway.
  2760. **
  2761. ** Limitations:  Does not work on floppies because they are too slow
  2762. **               Unknown if it will work on wierdo 3rd party file systems
  2763. **
  2764. ****************
  2765. */
  2766.  
  2767. /* There can only be 26 drive letters on NT */
  2768. #define _PR_MAX_DRIVES 26
  2769.  
  2770. _MDLock cachedVolumeLock;
  2771. DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
  2772. DWORD dwLastCachedDrive = 0;
  2773. DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
  2774.  
  2775. PRBool IsFileLocalInit()
  2776. {
  2777.    TCHAR lpBuffer[_PR_MAX_DRIVES*5];
  2778.    DWORD nBufferLength = _PR_MAX_DRIVES*5;
  2779.    DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
  2780.    DWORD dwIndex = 0;
  2781.    DWORD dwDriveType;
  2782.    DWORD dwVolumeSerialNumber;
  2783.    DWORD dwDriveIndex = 0;
  2784.    DWORD oldmode = (DWORD) -1;
  2785.  
  2786.    _MD_NEW_LOCK(&cachedVolumeLock);
  2787.  
  2788.    nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
  2789.    if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength)
  2790.       return PR_FALSE;
  2791.  
  2792.    // Calling GetVolumeInformation on a removeable drive where the
  2793.    // disk is currently removed will cause a dialog box to the
  2794.    // console.  This is not good.
  2795.    // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
  2796.    // damn dialog.
  2797.  
  2798.    dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
  2799.    oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
  2800.  
  2801.    // now loop through the logical drives
  2802.    while(lpBuffer[dwIndex] != TEXT('\0'))
  2803.    {
  2804.       // skip the floppy drives.  This is *SLOW*
  2805.       if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
  2806.          /* Skip over floppies */;
  2807.       else
  2808.       {
  2809.          dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
  2810.  
  2811.          dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
  2812.  
  2813.          switch(dwDriveType)
  2814.          {
  2815.                // Ignore these drive types
  2816.             case 0:
  2817.             case 1:
  2818.             case DRIVE_REMOTE:
  2819.             default: // If the drive type is unknown, ignore it.
  2820.                break;
  2821.  
  2822.                // Removable media drives can have different serial numbers
  2823.                // at different times, so cache the current serial number
  2824.                // but keep track of them so they can be rechecked if necessary.
  2825.             case DRIVE_REMOVABLE:
  2826.  
  2827.                // CDROM is a removable media
  2828.             case DRIVE_CDROM: 
  2829.  
  2830.                // no idea if ramdisks can change serial numbers or not
  2831.                // but it doesn't hurt to treat them as removable.
  2832.               
  2833.             case DRIVE_RAMDISK: 
  2834.  
  2835.  
  2836.                // Here is where we keep track of removable drives.
  2837.                dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
  2838.  
  2839.                // removable drives fall through to fixed drives and get cached.
  2840.  
  2841.             case DRIVE_FIXED:
  2842.  
  2843.                // cache volume serial numbers. 
  2844.                if (GetVolumeInformation(
  2845.                    &lpBuffer[dwIndex],
  2846.                    NULL, 0,
  2847.                    &dwVolumeSerialNumber,
  2848.                    NULL, NULL, NULL, 0)
  2849.                   )
  2850.                   {
  2851.                      if (dwLastCachedDrive < dwDriveIndex)
  2852.                         dwLastCachedDrive = dwDriveIndex;
  2853.                      dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
  2854.                   }
  2855.  
  2856.                break;
  2857.          }
  2858.       }
  2859.  
  2860.       dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
  2861.    }
  2862.  
  2863.    if (oldmode != (DWORD) -1) {
  2864.        SetErrorMode(oldmode);
  2865.        oldmode = (DWORD) -1;
  2866.    }
  2867.  
  2868.    return PR_TRUE;
  2869. }
  2870.  
  2871. PRInt32 IsFileLocal(HANDLE hFile)
  2872. {
  2873.    DWORD dwIndex = 0, dwMask;
  2874.    BY_HANDLE_FILE_INFORMATION Info;
  2875.    TCHAR szDrive[4] = TEXT("C:\\");
  2876.    DWORD dwVolumeSerialNumber;
  2877.    DWORD oldmode = (DWORD) -1;
  2878.    int rv = _PR_REMOTE_FILE;
  2879.  
  2880.    if (!GetFileInformationByHandle(hFile, &Info))
  2881.       return -1;
  2882.  
  2883.    // look to see if the volume serial number has been cached.
  2884.    _MD_LOCK(&cachedVolumeLock);
  2885.    while(dwIndex <= dwLastCachedDrive)
  2886.       if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
  2887.          return _PR_LOCAL_FILE;
  2888.    _MD_UNLOCK(&cachedVolumeLock);
  2889.  
  2890.    // volume serial number not found in the cache.  Check removable files.
  2891.    // removable drives are noted as a bitmask.  If the bit associated with 
  2892.    // a specific drive is set, then we should query its volume serial number
  2893.    // as its possible it has changed.
  2894.    dwMask = dwRemoveableDrivesToCheck;
  2895.    dwIndex = 0;
  2896.  
  2897.    while(dwMask)
  2898.    {
  2899.       while(!(dwMask & 1))
  2900.       {
  2901.          dwIndex++;
  2902.          dwMask = dwMask >> 1;
  2903.       }
  2904.  
  2905.       szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
  2906.  
  2907.       // Calling GetVolumeInformation on a removeable drive where the
  2908.       // disk is currently removed will cause a dialog box to the
  2909.       // console.  This is not good.
  2910.       // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
  2911.       // dialog.
  2912.  
  2913.       oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
  2914.  
  2915.       if (GetVolumeInformation(
  2916.                   szDrive,
  2917.                   NULL, 0,
  2918.                   &dwVolumeSerialNumber,
  2919.                   NULL, NULL, NULL, 0)
  2920.          )
  2921.       {
  2922.          if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
  2923.          {
  2924.             _MD_LOCK(&cachedVolumeLock);
  2925.             if (dwLastCachedDrive < dwIndex)
  2926.                dwLastCachedDrive = dwIndex;
  2927.             dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
  2928.             _MD_UNLOCK(&cachedVolumeLock);
  2929.             rv = _PR_LOCAL_FILE;
  2930.          }
  2931.       }
  2932.       if (oldmode != (DWORD) -1) {
  2933.           SetErrorMode(oldmode);
  2934.           oldmode = (DWORD) -1;
  2935.       }
  2936.  
  2937.       if (rv == _PR_LOCAL_FILE)
  2938.           return _PR_LOCAL_FILE;
  2939.  
  2940.       dwIndex++;
  2941.       dwMask = dwMask >> 1;
  2942.    }
  2943.  
  2944.    return _PR_REMOTE_FILE;
  2945. }
  2946. #endif /* _NEED_351_FILE_LOCKING_HACK */
  2947.  
  2948. void PR_NT_UseNonblock()
  2949. {
  2950.     _nt_use_async = 0;
  2951. }
  2952.  
  2953.  
  2954. PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime timeout)
  2955. {
  2956.     PRInt32 osfd = fd->secret->md.osfd;
  2957.     PRInt32 rv, err;
  2958.     fd_set rd;
  2959.     struct timeval tv, *tvp;
  2960.  
  2961.     FD_ZERO(&rd);
  2962.     FD_SET((SOCKET)osfd, &rd);
  2963.     if (timeout == PR_INTERVAL_NO_TIMEOUT) {
  2964.         while ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
  2965.             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  2966.                     && (!fd->secret->nonblocking)) {
  2967.                 if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
  2968.                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  2969.                     break;
  2970.                 }
  2971.             } else {
  2972.                 _PR_MD_MAP_ACCEPT_ERROR(err);
  2973.                 break;
  2974.         }
  2975.         }
  2976.         return(rv);
  2977.     } else if (timeout == PR_INTERVAL_NO_WAIT) {
  2978.         if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1)
  2979.             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  2980.                     && (!fd->secret->nonblocking)) {
  2981.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  2982.             } else {
  2983.                 _PR_MD_MAP_ACCEPT_ERROR(err);
  2984.             }
  2985.             return(rv);
  2986.         } else {
  2987. retry:
  2988.             if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
  2989.                 if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  2990.                         && (!fd->secret->nonblocking)) {
  2991.                     tv.tv_sec = PR_IntervalToSeconds(timeout);
  2992.                     tv.tv_usec = PR_IntervalToMicroseconds(
  2993.                         timeout - PR_SecondsToInterval(tv.tv_sec));
  2994.                     tvp = &tv;
  2995.  
  2996.                     rv = select(osfd + 1, &rd, NULL, NULL, tvp);
  2997.                     if (rv > 0) {
  2998.                         goto retry;
  2999.                     } else if (rv == 0) {
  3000.                         PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3001.                         rv = -1;
  3002.                     } else
  3003.                         _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3004.                 } else {
  3005.                     _PR_MD_MAP_ACCEPT_ERROR(err);
  3006.                 }
  3007.             }
  3008.         }
  3009.     return(rv);
  3010. }
  3011.  
  3012. PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
  3013. {
  3014.     PRInt32 osfd = fd->secret->md.osfd;
  3015.     PRInt32 rv, err;
  3016.     struct timeval tv, *tvp;
  3017.     fd_set rd;
  3018.  
  3019.     while ((rv = recv(osfd,buf,len,0)) == -1) {
  3020.         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3021.                 && (!fd->secret->nonblocking)) {
  3022.             FD_ZERO(&rd);
  3023.             FD_SET((SOCKET)osfd, &rd);
  3024.             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
  3025.                 tvp = NULL;
  3026.             } else {
  3027.                 tv.tv_sec = PR_IntervalToSeconds(timeout);
  3028.                 tv.tv_usec = PR_IntervalToMicroseconds(
  3029.                 timeout - PR_SecondsToInterval(tv.tv_sec));
  3030.                 tvp = &tv;
  3031.             }
  3032.             if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
  3033.                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3034.                 return -1;
  3035.             } else if (rv == 0) {
  3036.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3037.                 rv = -1;
  3038.                 break;
  3039.             }
  3040.         } else {
  3041.             _PR_MD_MAP_RECV_ERROR(err);
  3042.             break;
  3043.         }
  3044.     }
  3045.     return(rv);
  3046. }
  3047.  
  3048. PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
  3049. {
  3050.     PRInt32 osfd = fd->secret->md.osfd;
  3051.     PRInt32 rv, err;
  3052.     struct timeval tv, *tvp;
  3053.     fd_set wd;
  3054.     PRInt32 bytesSent = 0;
  3055.  
  3056.     while(bytesSent < len) {
  3057.         while ((rv = send(osfd,buf,len,0)) == -1) {
  3058.             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3059.                     && (!fd->secret->nonblocking)) {
  3060.                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
  3061.                     tvp = NULL;
  3062.                 } else {
  3063.                     tv.tv_sec = PR_IntervalToSeconds(timeout);
  3064.                     tv.tv_usec = PR_IntervalToMicroseconds(
  3065.                         timeout - PR_SecondsToInterval(tv.tv_sec));
  3066.                     tvp = &tv;
  3067.                 }
  3068.                 FD_ZERO(&wd);
  3069.                 FD_SET((SOCKET)osfd, &wd);
  3070.                 if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
  3071.                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3072.                     break;
  3073.                 }
  3074.                 if (rv == 0) {
  3075.                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3076.                     return -1;
  3077.                 }
  3078.             } else {
  3079.                 _PR_MD_MAP_SEND_ERROR(err);
  3080.                 return -1;
  3081.         }
  3082.         }
  3083.         bytesSent += rv;
  3084.         if (fd->secret->nonblocking) {
  3085.             break;
  3086.         }
  3087.         if ((rv >= 0) && (bytesSent < len)) {
  3088.             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
  3089.                 tvp = NULL;
  3090.             } else {
  3091.                 tv.tv_sec = PR_IntervalToSeconds(timeout);
  3092.                 tv.tv_usec = PR_IntervalToMicroseconds(
  3093.                     timeout - PR_SecondsToInterval(tv.tv_sec));
  3094.                 tvp = &tv;
  3095.             }
  3096.             FD_ZERO(&wd);
  3097.             FD_SET((SOCKET)osfd, &wd);
  3098.             if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
  3099.                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3100.                 break;
  3101.             }
  3102.             if (rv == 0) {
  3103.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3104.                 return -1;
  3105.             }
  3106.         }
  3107.     }
  3108.     return bytesSent;
  3109. }
  3110.  
  3111. PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime timeout)
  3112. {
  3113.     int index;
  3114.     int sent = 0;
  3115.     int rv;
  3116.  
  3117.     for (index=0; index<size; index++) {
  3118.         rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
  3119.         if (rv > 0) 
  3120.             sent += rv;
  3121.         if ( rv != iov[index].iov_len ) {
  3122.             if (rv <= 0) {
  3123.                 if (fd->secret->nonblocking
  3124.                         && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
  3125.                         && (sent > 0)) {
  3126.                     return sent;
  3127.                 } else {
  3128.                     return -1;
  3129.                 }
  3130.             }
  3131.             /* Only a nonblocking socket can have partial sends */
  3132.             PR_ASSERT(fd->secret->nonblocking);
  3133.             return sent;
  3134.         }
  3135.     }
  3136.  
  3137.     return sent;
  3138. }
  3139.  
  3140. PRInt32 _nt_nonblock_sendto(
  3141.     PRFileDesc *fd, const char *buf, int len,
  3142.     const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
  3143. {
  3144.     PRInt32 osfd = fd->secret->md.osfd;
  3145.     PRInt32 rv, err;
  3146.     struct timeval tv, *tvp;
  3147.     fd_set wd;
  3148.     PRInt32 bytesSent = 0;
  3149.  
  3150.     while(bytesSent < len) {
  3151.         while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
  3152.             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3153.                     && (!fd->secret->nonblocking)) {
  3154.                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
  3155.                     tvp = NULL;
  3156.                 } else {
  3157.                     tv.tv_sec = PR_IntervalToSeconds(timeout);
  3158.                     tv.tv_usec = PR_IntervalToMicroseconds(
  3159.                         timeout - PR_SecondsToInterval(tv.tv_sec));
  3160.                     tvp = &tv;
  3161.                 }
  3162.                 FD_ZERO(&wd);
  3163.                 FD_SET((SOCKET)osfd, &wd);
  3164.                 if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
  3165.                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3166.                     break;
  3167.                 }
  3168.                 if (rv == 0) {
  3169.                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3170.                     return -1;
  3171.                 }
  3172.             } else {
  3173.                 _PR_MD_MAP_SENDTO_ERROR(err);
  3174.                 return -1;
  3175.         }
  3176.         }
  3177.         bytesSent += rv;
  3178.         if (fd->secret->nonblocking) {
  3179.             break;
  3180.         }
  3181.         if ((rv >= 0) && (bytesSent < len)) {
  3182.             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
  3183.                 tvp = NULL;
  3184.             } else {
  3185.                 tv.tv_sec = PR_IntervalToSeconds(timeout);
  3186.                 tv.tv_usec = PR_IntervalToMicroseconds(
  3187.                     timeout - PR_SecondsToInterval(tv.tv_sec));
  3188.                 tvp = &tv;
  3189.             }
  3190.             FD_ZERO(&wd);
  3191.             FD_SET((SOCKET)osfd, &wd);
  3192.             if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
  3193.                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3194.                 break;
  3195.             }
  3196.             if (rv == 0) {
  3197.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3198.                 return -1;
  3199.             }
  3200.         }
  3201.     }
  3202.     return bytesSent;
  3203. }
  3204.  
  3205. PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
  3206. {
  3207.     PRInt32 osfd = fd->secret->md.osfd;
  3208.     PRInt32 rv, err;
  3209.     struct timeval tv, *tvp;
  3210.     fd_set rd;
  3211.  
  3212.     while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
  3213.         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3214.                 && (!fd->secret->nonblocking)) {
  3215.             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
  3216.                 tvp = NULL;
  3217.             } else {
  3218.                 tv.tv_sec = PR_IntervalToSeconds(timeout);
  3219.                 tv.tv_usec = PR_IntervalToMicroseconds(
  3220.                 timeout - PR_SecondsToInterval(tv.tv_sec));
  3221.                 tvp = &tv;
  3222.             }
  3223.             FD_ZERO(&rd);
  3224.             FD_SET((SOCKET)osfd, &rd);
  3225.             if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
  3226.                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
  3227.                 return -1;
  3228.             } else if (rv == 0) {
  3229.                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
  3230.                 rv = -1;
  3231.                 break;
  3232.             }
  3233.         } else {
  3234.             _PR_MD_MAP_RECVFROM_ERROR(err);
  3235.             break;
  3236.         }
  3237.     }
  3238.     return(rv);
  3239. }
  3240.  
  3241. /*
  3242.  * UDP support: the continuation thread functions and recvfrom and sendto.
  3243.  */
  3244.  
  3245. static void pt_InsertTimedInternal(pt_Continuation *op)
  3246. {
  3247.     PRInt32 delta = 0;
  3248.     pt_Continuation *t_op = NULL;
  3249.     PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
  3250.  
  3251.     /*
  3252.      * If this element operation isn't timed, it gets queued at the
  3253.      * end of the list (just after pt_tq.tail) and we're
  3254.      * finishd early.
  3255.      */
  3256.     if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
  3257.     {
  3258.         t_op = pt_tq.tail;  /* put it at the end */
  3259.         goto done;
  3260.     }
  3261.  
  3262.     /*
  3263.      * The rest of this routine actaully deals with timed ops.
  3264.      */
  3265.  
  3266.     if (NULL != pt_tq.op)
  3267.     {
  3268.         /*
  3269.          * To find where in the list to put the new operation, form
  3270.          * the absolute time the operations in question will expire.
  3271.          *
  3272.          * The new operation ('op') will expire at now() + op->timeout.
  3273.          *
  3274.          * The operation that will time out furthest in the future will
  3275.          * do so at pt_tq.epoch + pt_tq.op->timeout.
  3276.          *
  3277.          * Subsequently earlier timeouts are computed based on the latter
  3278.          * knowledge by subracting the timeout deltas that are stored in
  3279.          * the operation list. There are operation[n]->timeout ticks
  3280.          * between the expiration of operation[n-1] and operation[n].e e 
  3281.          *
  3282.          * Therefore, the operation[n-1] will expire operation[n]->timeout
  3283.          * ticks prior to operation[n].
  3284.          *
  3285.          * This should be easy!
  3286.          */
  3287.         t_op = pt_tq.op;  /* running pointer to queued op */
  3288.         op_tmo = now + op->timeout;  /* that's in absolute ticks */
  3289.         qd_tmo = pt_tq.epoch + t_op->timeout;  /* likewise */
  3290.  
  3291.         do
  3292.         {
  3293.             /*
  3294.              * If 'op' expires later than t_op, then insert 'op' just
  3295.              * ahead of t_op. Otherwise, compute when operation[n-1]
  3296.              * expires and try again.
  3297.              *
  3298.              * The actual different between the expiriation of 'op'
  3299.              * and the current operation what becomes the new operaton's
  3300.              * timeout interval. That interval is also subtracted from
  3301.              * the interval of the operation immediately following where
  3302.              * we stick 'op' (unless the next one isn't timed). The new
  3303.              * timeout assigned to 'op' takes into account the values of
  3304.              * now() and when the previous intervals were compured.
  3305.              */
  3306.             delta = op_tmo - qd_tmo;
  3307.             if (delta >= 0)
  3308.             {
  3309.                 op->timeout += (now - pt_tq.epoch);
  3310.                 goto done;
  3311.             }
  3312.  
  3313.             qd_tmo -= t_op->timeout;  /* previous operaton expiration */
  3314.             t_op = t_op->prev;  /* point to previous operation */
  3315.             if (NULL != t_op) qd_tmo += t_op->timeout;
  3316.         } while (NULL != t_op);
  3317.  
  3318.         /*
  3319.          * If we got here we backed off the head of the list. That means that
  3320.          * this timed entry has to go at the head of the list. This is just
  3321.          * about like having an empty timer list.
  3322.          */
  3323.         delta = op->timeout;  /* $$$ is this right? */
  3324.     }
  3325.  
  3326. done:
  3327.  
  3328.     /*
  3329.      * Insert 'op' into the queue just after t_op or if t_op is null,
  3330.      * at the head of the list.
  3331.      *
  3332.      * If t_op is NULL, the list is currently empty and this is pretty
  3333.      * easy.
  3334.      */
  3335.     if (NULL == t_op)
  3336.     {
  3337.         op->prev = NULL;
  3338.         op->next = pt_tq.head;
  3339.         pt_tq.head = op;
  3340.         if (NULL == pt_tq.tail) pt_tq.tail = op;
  3341.         else op->next->prev = op;
  3342.     }
  3343.     else
  3344.     {
  3345.         op->prev = t_op;
  3346.         op->next = t_op->next;
  3347.         if (NULL != op->prev)
  3348.             op->prev->next = op;
  3349.         if (NULL != op->next)
  3350.             op->next->prev = op;
  3351.         if (t_op == pt_tq.tail)
  3352.             pt_tq.tail = op;
  3353.     }
  3354.  
  3355.     /*
  3356.      * Are we adjusting our epoch, etc? Are we replacing
  3357.      * what was previously the element due to expire furthest
  3358.      * out in the future? Is this even a timed operation?
  3359.      */
  3360.     if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
  3361.     {
  3362.         if ((NULL == pt_tq.op)  /* we're the one and only */
  3363.         || (t_op == pt_tq.op))  /* we're replacing */
  3364.         {
  3365.             pt_tq.op = op;
  3366.             pt_tq.epoch = now;
  3367.         }
  3368.     }
  3369.  
  3370.     pt_tq.op_count += 1;
  3371.  
  3372. }  /* pt_InsertTimedInternal */
  3373.  
  3374. /*
  3375.  * function: pt_FinishTimed
  3376.  *
  3377.  * Takes the finished operation out of the timed queue. It
  3378.  * notifies the initiating thread that the opertions is
  3379.  * complete and returns to the caller the value of the next
  3380.  * operation in the list (or NULL).
  3381.  */
  3382. static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
  3383. {
  3384.     pt_Continuation *next;
  3385.  
  3386.     /* remove this one from the list */
  3387.     if (NULL == op->prev) pt_tq.head = op->next;
  3388.     else op->prev->next = op->next;
  3389.     if (NULL == op->next) pt_tq.tail = op->prev;
  3390.     else op->next->prev = op->prev;
  3391.  
  3392.     /* did we happen to hit the timed op? */
  3393.     if (op == pt_tq.op) pt_tq.op = op->prev;
  3394.  
  3395.     next = op->next;
  3396.     op->next = op->prev = NULL;
  3397.     op->status = pt_continuation_done;
  3398.  
  3399.     pt_tq.op_count -= 1;
  3400. #if defined(DEBUG)
  3401.     pt_debug.continuationsServed += 1;
  3402. #endif
  3403.     PR_NotifyCondVar(op->complete);
  3404.  
  3405.     return next;
  3406. }  /* pt_FinishTimedInternal */
  3407.  
  3408. static void ContinuationThread(void *arg)
  3409. {
  3410.     /* initialization */
  3411.     fd_set readSet, writeSet, exceptSet;
  3412.     struct timeval tv;
  3413.     SOCKET *pollingList = 0;                /* list built for polling */
  3414.     PRIntn pollingListUsed;                 /* # entries used in the list */
  3415.     PRIntn pollingListNeeded;               /* # entries needed this time */
  3416.     PRIntn pollingSlotsAllocated = 0;       /* # entries available in list */
  3417.     PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
  3418.  
  3419.     /* do some real work */
  3420.     while (1)
  3421.     {
  3422.         PRIntn rv;
  3423.         PRStatus status;
  3424.         PRIntn pollIndex;
  3425.         pt_Continuation *op;
  3426.         PRIntervalTime now = PR_IntervalNow();
  3427.         PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
  3428.  
  3429.         PR_Lock(pt_tq.ml);
  3430.         while (NULL == pt_tq.head)
  3431.         {
  3432.             status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
  3433.             if ((PR_FAILURE == status)
  3434.                 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
  3435.         }
  3436.         pollingListNeeded = pt_tq.op_count;
  3437.         PR_Unlock(pt_tq.ml);
  3438.  
  3439.         /* Okay. We're history */
  3440.         if ((PR_FAILURE == status)
  3441.             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
  3442.  
  3443.     /*
  3444.      * We are not holding the pt_tq.ml lock now, so more items may
  3445.      * get added to pt_tq during this window of time.  We hope
  3446.      * that 10 more spaces in the polling list should be enough.
  3447.      */
  3448.  
  3449.         FD_ZERO(&readSet);
  3450.         FD_ZERO(&writeSet);
  3451.         FD_ZERO(&exceptSet);
  3452.         pollingListNeeded += 10;
  3453.         if (pollingListNeeded > pollingSlotsAllocated)
  3454.         {
  3455.             if (NULL != pollingList) PR_DELETE(pollingList);
  3456.             pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
  3457.             PR_ASSERT(NULL != pollingList);
  3458.             pollingSlotsAllocated = pollingListNeeded;
  3459.         }
  3460.  
  3461. #if defined(DEBUG)
  3462.         if (pollingListNeeded > pt_debug.pollingListMax)
  3463.             pt_debug.pollingListMax = pollingListUsed;
  3464. #endif
  3465.  
  3466.         /*
  3467.          * Build up a polling list.
  3468.          * This list is sorted on time. Operations that have been
  3469.          * interrupted are completed and not included in the list.
  3470.          * There is an assertion that the operation is in progress.
  3471.          */
  3472.         pollingListUsed = 0;
  3473.         PR_Lock(pt_tq.ml);
  3474.  
  3475.         for (op = pt_tq.head; NULL != op;)
  3476.         {
  3477.             if (pt_continuation_abort == op->status)
  3478.             {
  3479.                 op->result.code = -1;
  3480.                 op->syserrno = WSAEINTR;
  3481.                 op = pt_FinishTimedInternal(op);
  3482.             }
  3483.             else
  3484.             {
  3485.                 PR_ASSERT(pt_continuation_done != op->status);
  3486.                 op->status = pt_continuation_inprogress;
  3487.                 if (op->event & PR_POLL_READ) {
  3488.                     FD_SET(op->arg1.osfd, &readSet);
  3489.                 }
  3490.                 if (op->event & PR_POLL_WRITE) {
  3491.                     FD_SET(op->arg1.osfd, &writeSet);
  3492.                 }
  3493.                 if (op->event & PR_POLL_EXCEPT) {
  3494.                     FD_SET(op->arg1.osfd, &exceptSet);
  3495.                 }
  3496.                 pollingList[pollingListUsed] = op->arg1.osfd;
  3497.                 pollingListUsed += 1;
  3498.                 if (pollingListUsed == pollingSlotsAllocated) break;
  3499.                 op = op->next;
  3500.             }
  3501.         }
  3502.  
  3503.         PR_Unlock(pt_tq.ml);
  3504.  
  3505.         /*
  3506.          * If 'op' isn't NULL at this point, then we didn't get to
  3507.          * the end of the list. That means that more items got added
  3508.          * to the list than we anticipated. So, forget this iteration,
  3509.          * go around the horn again.
  3510.          * One would hope this doesn't happen all that often.
  3511.          */
  3512.         if (NULL != op)
  3513.         {
  3514. #if defined(DEBUG)
  3515.             pt_debug.predictionsFoiled += 1;  /* keep track */
  3516. #endif
  3517.             continue;  /* make it rethink things */
  3518.         }
  3519.  
  3520.         /* there's a chance that all ops got blown away */
  3521.         if (NULL == pt_tq.head) continue;
  3522.         /* if not, we know this is the shortest timeout */
  3523.         timeout = pt_tq.head->timeout;
  3524.  
  3525.         /*
  3526.          * We don't want to wait forever on this poll. So keep
  3527.          * the interval down. The operations, if they are timed,
  3528.          * still have to timeout, while those that are not timed
  3529.          * should persist forever. But they may be aborted. That's
  3530.          * what this anxiety is all about.
  3531.          */
  3532.         if (timeout > mx_select_ticks) timeout = mx_select_ticks;
  3533.  
  3534.         if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)
  3535.             pt_tq.head->timeout -= timeout;
  3536.         tv.tv_sec = PR_IntervalToSeconds(timeout);
  3537.         tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
  3538.  
  3539.         rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
  3540.  
  3541.         if (0 == rv)  /* poll timed out - what about leading op? */
  3542.         {
  3543.             if (0 == pt_tq.head->timeout)
  3544.             {
  3545.                 /* 
  3546.                  * The leading element of the timed queue has timed
  3547.                  * out. Get rid of it. In any case go around the
  3548.                  * loop again, computing the polling list, checking
  3549.                  * for interrupted operations.
  3550.                  */
  3551.                 PR_Lock(pt_tq.ml);
  3552.                 do
  3553.                 {
  3554.                     pt_tq.head->result.code = -1;
  3555.                     pt_tq.head->syserrno = WSAETIMEDOUT;
  3556.                     op = pt_FinishTimedInternal(pt_tq.head);
  3557.                 } while ((NULL != op) && (0 == op->timeout));
  3558.                 PR_Unlock(pt_tq.ml);
  3559.             }
  3560.             continue;
  3561.         }
  3562.  
  3563.         if (-1 == rv && (WSAGetLastError() == WSAEINTR
  3564.                 || WSAGetLastError() == WSAEINPROGRESS))
  3565.         {
  3566.             continue;               /* go around the loop again */
  3567.         }
  3568.  
  3569.         /*
  3570.          * select() says that something in our list is ready for some more
  3571.          * action or is an invalid fd. Find it, load up the operation and
  3572.          * see what happens.
  3573.          */
  3574.  
  3575.         PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
  3576.  
  3577.  
  3578.         /*
  3579.          * $$$ There's a problem here. I'm running the operations list
  3580.          * and I'm not holding any locks. I don't want to hold the lock
  3581.          * and do the operation, so this is really messed up..
  3582.          *
  3583.          * This may work out okay. The rule is that only this thread,
  3584.          * the continuation thread, can remove elements from the list.
  3585.          * Therefore, the list is at worst, longer than when we built
  3586.          * the polling list.
  3587.          */
  3588.         op = pt_tq.head;
  3589.         for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
  3590.         {
  3591.             PRInt16 revents = 0;
  3592.  
  3593.             PR_ASSERT(NULL != op);
  3594.  
  3595.             /*
  3596.              * This one wants attention. Redo the operation.
  3597.              * We know that there can only be more elements
  3598.              * in the op list than we knew about when we created
  3599.              * the poll list. Therefore, we might have to skip
  3600.              * a few ops to find the right one to operation on.
  3601.              */
  3602.             while (pollingList[pollIndex] != op->arg1.osfd )
  3603.             {
  3604.                 op = op->next;
  3605.                 PR_ASSERT(NULL != op);
  3606.             }
  3607.  
  3608.             if (FD_ISSET(op->arg1.osfd, &readSet)) {
  3609.                 revents |= PR_POLL_READ;
  3610.             }
  3611.             if (FD_ISSET(op->arg1.osfd, &writeSet)) {
  3612.                 revents |= PR_POLL_WRITE;
  3613.             }
  3614.             if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
  3615.                 revents |= PR_POLL_EXCEPT;
  3616.             }
  3617.  
  3618.             /*
  3619.              * Sip over all those not in progress. They'll be
  3620.              * pruned next time we build a polling list. Call
  3621.              * the continuation function. If it reports completion,
  3622.              * finish off the operation.
  3623.              */
  3624.             if (revents && (pt_continuation_inprogress == op->status)
  3625.                 && (op->function(op, revents)))
  3626.             {
  3627.                 PR_Lock(pt_tq.ml);
  3628.                 op = pt_FinishTimedInternal(op);
  3629.                 PR_Unlock(pt_tq.ml);
  3630.             }
  3631.         }
  3632.     }
  3633.     if (NULL != pollingList) PR_DELETE(pollingList);
  3634. }  /* ContinuationThread */
  3635.  
  3636. static int pt_Continue(pt_Continuation *op)
  3637. {
  3638.     PRStatus rv;
  3639.     /* Finish filling in the blank slots */
  3640.     op->status = pt_continuation_sumbitted;
  3641.     op->complete = PR_NewCondVar(pt_tq.ml);
  3642.  
  3643.     PR_Lock(pt_tq.ml);  /* we provide the locking */
  3644.  
  3645.     pt_InsertTimedInternal(op);  /* insert in the structure */
  3646.  
  3647.     PR_NotifyCondVar(pt_tq.new_op);  /* notify the continuation thread */
  3648.  
  3649.     while (pt_continuation_done != op->status)  /* wait for completion */
  3650.     {
  3651.         rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
  3652.         /*
  3653.          * If we get interrupted, we set state the continuation thread will
  3654.          * see and allow it to finish the I/O operation w/ error. That way
  3655.          * the rule that only the continuation thread is removing elements
  3656.          * from the list is still valid.
  3657.          *
  3658.          * Don't call interrupt on the continuation thread. That'll just
  3659.          * piss him off. He's cycling around at least every mx_select_ticks
  3660.          * anyhow and should notice the request in there.
  3661.          */
  3662.         if ((PR_FAILURE == rv)
  3663.             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
  3664.             op->status = pt_continuation_abort;  /* our status */
  3665.     }
  3666.  
  3667.     PR_Unlock(pt_tq.ml);  /* we provide the locking */
  3668.  
  3669.     PR_DestroyCondVar(op->complete);
  3670.  
  3671.     return op->result.code;  /* and the primary answer */
  3672. }  /* pt_Continue */
  3673.  
  3674. static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
  3675. {
  3676.     PRIntn bytes = sendto(
  3677.         op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
  3678.         (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
  3679.     op->syserrno = WSAGetLastError();
  3680.     if (bytes > 0)  /* this is progress */
  3681.     {
  3682.         char *bp = op->arg2.buffer;
  3683.         bp += bytes;  /* adjust the buffer pointer */
  3684.         op->arg2.buffer = bp;
  3685.         op->result.code += bytes;  /* accumulate the number sent */
  3686.         op->arg3.amount -= bytes;  /* and reduce the required count */
  3687.         return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
  3688.     }
  3689.     else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
  3690.     PR_FALSE : PR_TRUE;
  3691. }  /* pt_sendto_cont */
  3692.  
  3693. static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
  3694. {
  3695.     PRIntn addr_len = sizeof(*(op->arg5.addr));
  3696.     op->result.code = recvfrom(
  3697.         op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
  3698.         op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
  3699.     op->syserrno = WSAGetLastError();
  3700.     return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
  3701.         PR_FALSE : PR_TRUE;
  3702. }  /* pt_recvfrom_cont */
  3703.  
  3704. static PRInt32 pt_SendTo(
  3705.     SOCKET osfd, const void *buf,
  3706.     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
  3707.     PRIntn addrlen, PRIntervalTime timeout)
  3708. {
  3709.     PRInt32 bytes = -1, err;
  3710.     PRBool fNeedContinue = PR_FALSE;
  3711.  
  3712.     bytes = sendto(
  3713.             osfd, buf, amount, flags,
  3714.             (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
  3715.     if (bytes == -1) {
  3716.         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3717.         fNeedContinue = PR_TRUE;
  3718.         else
  3719.             _PR_MD_MAP_SENDTO_ERROR(err);
  3720.     }
  3721.     if (fNeedContinue == PR_TRUE)
  3722.     {
  3723.         pt_Continuation op;
  3724.         op.arg1.osfd = osfd;
  3725.         op.arg2.buffer = (void*)buf;
  3726.         op.arg3.amount = amount;
  3727.         op.arg4.flags = flags;
  3728.         op.arg5.addr = addr;
  3729.         op.timeout = timeout;
  3730.         op.result.code = 0;  /* initialize the number sent */
  3731.         op.function = pt_sendto_cont;
  3732.         op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
  3733.         bytes = pt_Continue(&op);
  3734.         if (bytes < 0) {
  3735.             WSASetLastError(op.syserrno);
  3736.             _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
  3737.         }
  3738.     }
  3739.     return bytes;
  3740. }  /* pt_SendTo */
  3741.  
  3742. static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
  3743.     PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
  3744. {
  3745.     PRInt32 bytes = -1, err;
  3746.     PRBool fNeedContinue = PR_FALSE;
  3747.  
  3748.     bytes = recvfrom(
  3749.             osfd, buf, amount, flags,
  3750.             (struct sockaddr*)addr, addr_len);
  3751.     if (bytes == -1) {
  3752.         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
  3753.         fNeedContinue = PR_TRUE;
  3754.         else
  3755.             _PR_MD_MAP_RECVFROM_ERROR(err);
  3756.     }
  3757.  
  3758.     if (fNeedContinue == PR_TRUE)
  3759.     {
  3760.         pt_Continuation op;
  3761.         op.arg1.osfd = osfd;
  3762.         op.arg2.buffer = buf;
  3763.         op.arg3.amount = amount;
  3764.         op.arg4.flags = flags;
  3765.         op.arg5.addr = addr;
  3766.         op.timeout = timeout;
  3767.         op.function = pt_recvfrom_cont;
  3768.         op.event = PR_POLL_READ | PR_POLL_EXCEPT;
  3769.         bytes = pt_Continue(&op);
  3770.         if (bytes < 0) {
  3771.             WSASetLastError(op.syserrno);
  3772.             _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
  3773.         }
  3774.     }
  3775.     return bytes;
  3776. }  /* pt_RecvFrom */
  3777.