Back to index

lightning-sunbird  0.9+nobinonly
ntio.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Masayuki Nakano <masayuki@d-toybox.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 /* Windows NT IO module
00040  *
00041  * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
00042  * For LOCAL_SCOPE threads, we're using NT fibers.  For GLOBAL_SCOPE threads
00043  * we're using NT-native threads.
00044  *
00045  * When doing IO, we want to use completion ports for optimal performance 
00046  * with fibers.  But if we use completion ports for all IO, it is difficult
00047  * to project a blocking model with GLOBAL_SCOPE threads.  To handle this
00048  * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
00049  * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
00050  * threads because it means extra context switches, which are really slow
00051  * on NT...  Since we're using a single completion port, some IO will
00052  * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
00053  * extra context switching; but I don't think there is anything I can do
00054  * about it.
00055  */
00056 
00057 #include "primpl.h"
00058 #include "pprmwait.h"
00059 #include <direct.h>
00060 #include <mbstring.h>
00061 
00062 static HANDLE                _pr_completion_port;
00063 static PRThread             *_pr_io_completion_thread;
00064 
00065 #define RECYCLE_SIZE 512
00066 static struct _MDLock        _pr_recycle_lock;
00067 static PRInt32               _pr_recycle_INET_array[RECYCLE_SIZE];
00068 static PRInt32               _pr_recycle_INET_tail = 0; 
00069 static PRInt32               _pr_recycle_INET6_array[RECYCLE_SIZE];
00070 static PRInt32               _pr_recycle_INET6_tail = 0; 
00071 
00072 __declspec(thread) PRThread *_pr_io_restarted_io = NULL;
00073 DWORD _pr_io_restartedIOIndex;  /* The thread local storage slot for each
00074                                  * thread is initialized to NULL. */
00075 
00076 PRBool                       _nt_version_gets_lockfile_completion;
00077 
00078 struct _MDLock               _pr_ioq_lock;
00079 extern _MDLock               _nt_idleLock;
00080 extern PRCList               _nt_idleList;
00081 extern PRUint32              _nt_idleCount;
00082 
00083 #define CLOSE_TIMEOUT   PR_SecondsToInterval(5)
00084 
00085 /*
00086  * NSPR-to-NT access right mapping table for files.
00087  */
00088 static DWORD fileAccessTable[] = {
00089     FILE_GENERIC_READ,
00090     FILE_GENERIC_WRITE,
00091     FILE_GENERIC_EXECUTE
00092 };
00093 
00094 /*
00095  * NSPR-to-NT access right mapping table for directories.
00096  */
00097 static DWORD dirAccessTable[] = {
00098     FILE_GENERIC_READ,
00099     FILE_GENERIC_WRITE|FILE_DELETE_CHILD,
00100     FILE_GENERIC_EXECUTE
00101 };
00102 
00103 /*
00104  * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
00105  * We store the value in a PRTime variable for convenience.
00106  * This constant is used by _PR_FileTimeToPRTime().
00107  */
00108 #ifdef __GNUC__
00109 static const PRTime _pr_filetime_offset = 116444736000000000LL;
00110 #else
00111 static const PRTime _pr_filetime_offset = 116444736000000000i64;
00112 #endif
00113 
00114 static PRBool IsPrevCharSlash(const char *str, const char *current);
00115 
00116 #define _NEED_351_FILE_LOCKING_HACK
00117 #ifdef _NEED_351_FILE_LOCKING_HACK
00118 #define _PR_LOCAL_FILE 1
00119 #define _PR_REMOTE_FILE 2
00120 PRBool IsFileLocalInit();
00121 PRInt32 IsFileLocal(HANDLE hFile);
00122 #endif /* _NEED_351_FILE_LOCKING_HACK */
00123 
00124 static PRInt32 _md_MakeNonblock(HANDLE);
00125 
00126 static PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime);
00127 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime);
00128 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime);
00129 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
00130 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime);
00131 static PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
00132 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
00133 
00134 /*
00135  * We cannot associate a fd (a socket) with an I/O completion port
00136  * if the fd is nonblocking or inheritable.
00137  *
00138  * Nonblocking socket I/O won't work if the socket is associated with
00139  * an I/O completion port.
00140  *
00141  * An inheritable fd cannot be associated with an I/O completion port
00142  * because the completion notification of async I/O initiated by the
00143  * child process is still posted to the I/O completion port in the
00144  * parent process. 
00145  */
00146 #define _NT_USE_NB_IO(fd) \
00147     ((fd)->secret->nonblocking || (fd)->secret->inheritable == _PR_TRI_TRUE)
00148 
00149 /*
00150  * UDP support
00151  * 
00152  * UDP is supported on NT by the continuation thread mechanism.
00153  * The code is borrowed from ptio.c in pthreads nspr, hence the
00154  * PT and pt prefixes.  This mechanism is in fact general and
00155  * not limited to UDP.  For now, only UDP's recvfrom and sendto
00156  * go through the continuation thread if they get WSAEWOULDBLOCK
00157  * on first try.  Recv and send on a connected UDP socket still
00158  * goes through asychronous io.
00159  */
00160 
00161 #define PT_DEFAULT_SELECT_MSEC 100
00162 
00163 typedef struct pt_Continuation pt_Continuation;
00164 typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
00165 
00166 typedef enum pr_ContuationStatus
00167 {
00168     pt_continuation_sumbitted,
00169     pt_continuation_inprogress,
00170     pt_continuation_abort,
00171     pt_continuation_done
00172 } pr_ContuationStatus;
00173 
00174 struct pt_Continuation
00175 {
00176     /* These objects are linked in ascending timeout order */
00177     pt_Continuation *next, *prev;           /* self linked list of these things */
00178 
00179     /* The building of the continuation operation */
00180     ContinuationFn function;                /* what function to continue */
00181     union { SOCKET osfd; } arg1;            /* #1 - the op's fd */
00182     union { void* buffer; } arg2;           /* #2 - primary transfer buffer */
00183     union { PRIntn amount; } arg3;          /* #3 - size of 'buffer' */
00184     union { PRIntn flags; } arg4;           /* #4 - read/write flags */
00185     union { PRNetAddr *addr; } arg5;        /* #5 - send/recv address */
00186     
00187     PRIntervalTime timeout;                 /* representation of the timeout */
00188 
00189     PRIntn event;                           /* flags for select()'s events */
00190 
00191     /*
00192     ** The representation and notification of the results of the operation.
00193     ** These function can either return an int return code or a pointer to
00194     ** some object.
00195     */
00196     union { PRIntn code; void *object; } result;
00197 
00198     PRIntn syserrno;                        /* in case it failed, why (errno) */
00199     pr_ContuationStatus status;             /* the status of the operation */
00200     PRCondVar *complete;                    /* to notify the initiating thread */
00201 };
00202 
00203 static struct pt_TimedQueue
00204 {
00205     PRLock *ml;                             /* a little protection */
00206     PRThread *thread;                       /* internal thread's identification */
00207     PRCondVar *new_op;                      /* new operation supplied */
00208     PRCondVar *finish_op;                   /* an existing operation finished */
00209     PRUintn op_count;                       /* number of operations in the list */
00210     pt_Continuation *head, *tail;           /* head/tail of list of operations */
00211 
00212     pt_Continuation *op;                    /* timed operation furthest in future */
00213     PRIntervalTime epoch;                   /* the epoch of 'timed' */
00214 } pt_tq;
00215 
00216 #if defined(DEBUG)
00217 static struct pt_debug_s
00218 {
00219     PRIntn predictionsFoiled;
00220     PRIntn pollingListMax;
00221     PRIntn continuationsServed;
00222 } pt_debug;
00223 #endif  /* DEBUG */
00224 
00225 static void ContinuationThread(void *arg);
00226 static PRInt32 pt_SendTo(
00227     SOCKET osfd, const void *buf,
00228     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
00229     PRIntn addrlen, PRIntervalTime timeout);
00230 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
00231     PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
00232 
00233 
00234 /* The key returned from GetQueuedCompletionStatus() is used to determine what
00235  * type of completion we have.  We differentiate between IO completions and
00236  * CVAR completions.
00237  */
00238 #define KEY_IO              0xaaaaaaaa
00239 #define KEY_CVAR            0xbbbbbbbb
00240 
00241 PRInt32
00242 _PR_MD_PAUSE_CPU(PRIntervalTime ticks)
00243 {
00244     int awoken = 0;
00245     unsigned long bytes, key;
00246     int rv;
00247     LPOVERLAPPED olp;
00248     _MDOverlapped *mdOlp;
00249     PRUint32 timeout;
00250 
00251     if (_nt_idleCount > 0) {
00252         PRThread *deadThread;
00253 
00254         _MD_LOCK(&_nt_idleLock);
00255         while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
00256             deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
00257             PR_REMOVE_LINK(&deadThread->links);
00258 
00259             PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
00260 
00261             /* XXXMB - cleanup to do here? */
00262             if ( !_PR_IS_NATIVE_THREAD(deadThread) ){
00263                 /* Spinlock while user thread is still running.
00264                  * There is no way to use a condition variable here. The thread
00265                  * is dead, and we have to wait until we switch off the dead 
00266                  * thread before we can kill the fiber completely.
00267                  */
00268                 while ( deadThread->no_sched)
00269                     ;
00270 
00271                 DeleteFiber(deadThread->md.fiber_id);
00272             }
00273             memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
00274             if (!deadThread->threadAllocatedOnStack)
00275                 PR_DELETE(deadThread);
00276             _nt_idleCount--;
00277         }
00278         _MD_UNLOCK(&_nt_idleLock);
00279     }
00280 
00281     if (ticks == PR_INTERVAL_NO_TIMEOUT)
00282 #if 0
00283         timeout = INFINITE;
00284 #else
00285     /*
00286      * temporary hack to poll the runq every 5 seconds because of bug in
00287      * native threads creating user threads and not poking the right cpu.
00288      *
00289      * A local thread that was interrupted is bound to its current
00290      * cpu but there is no easy way for the interrupter to poke the
00291      * right cpu.  This is a hack to poll the runq every 5 seconds.
00292      */
00293         timeout = 5000;
00294 #endif
00295     else 
00296         timeout = PR_IntervalToMilliseconds(ticks);
00297 
00298     /*
00299      * The idea of looping here is to complete as many IOs as possible before
00300      * returning.  This should minimize trips to the idle thread.
00301      */
00302     while(1) {
00303         rv = GetQueuedCompletionStatus(
00304                 _pr_completion_port,
00305                 &bytes,
00306                 &key,
00307                 &olp,
00308                 timeout); 
00309         if (rv == 0 && olp == NULL) {
00310             /* Error in GetQueuedCompetionStatus */
00311             if (GetLastError() != WAIT_TIMEOUT) {
00312                 /* ARGH - what can we do here? Log an error? XXXMB */
00313                 return -1;
00314             } else {
00315                 /* If awoken == 0, then we just had a timeout */
00316                 return awoken;
00317             }
00318         }
00319 
00320         if (olp == NULL) 
00321             return 0;
00322 
00323         mdOlp = (_MDOverlapped *)olp;
00324 
00325         if (mdOlp->ioModel == _MD_MultiWaitIO) {
00326             PRRecvWait *desc;
00327             PRWaitGroup *group;
00328             PRThread *thred = NULL;
00329             PRMWStatus mwstatus;
00330 
00331             desc = mdOlp->data.mw.desc;
00332             PR_ASSERT(desc != NULL);
00333             mwstatus = rv ? PR_MW_SUCCESS : PR_MW_FAILURE;
00334             if (InterlockedCompareExchange((PVOID *)&desc->outcome,
00335                     (PVOID)mwstatus, (PVOID)PR_MW_PENDING)
00336                     == (PVOID)PR_MW_PENDING) {
00337                 if (mwstatus == PR_MW_SUCCESS) {
00338                     desc->bytesRecv = bytes;
00339                 } else {
00340                     mdOlp->data.mw.error = GetLastError();
00341                 }
00342             }
00343             group = mdOlp->data.mw.group;
00344             PR_ASSERT(group != NULL);
00345 
00346             _PR_MD_LOCK(&group->mdlock);
00347             PR_APPEND_LINK(&mdOlp->data.mw.links, &group->io_ready);
00348             PR_ASSERT(desc->fd != NULL);
00349             NT_HashRemoveInternal(group, desc->fd);
00350             if (!PR_CLIST_IS_EMPTY(&group->wait_list)) {
00351                 thred = _PR_THREAD_CONDQ_PTR(PR_LIST_HEAD(&group->wait_list));
00352                 PR_REMOVE_LINK(&thred->waitQLinks);
00353             }
00354             _PR_MD_UNLOCK(&group->mdlock);
00355 
00356             if (thred) {
00357                 if (!_PR_IS_NATIVE_THREAD(thred)) {
00358                     int pri = thred->priority;
00359                     _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
00360                     _PR_THREAD_LOCK(thred);
00361                     if (thred->flags & _PR_ON_PAUSEQ) {
00362                         _PR_SLEEPQ_LOCK(thred->cpu);
00363                         _PR_DEL_SLEEPQ(thred, PR_TRUE);
00364                         _PR_SLEEPQ_UNLOCK(thred->cpu);
00365                         _PR_THREAD_UNLOCK(thred);
00366                         thred->cpu = lockedCPU;
00367                         thred->state = _PR_RUNNABLE;
00368                         _PR_RUNQ_LOCK(lockedCPU);
00369                         _PR_ADD_RUNQ(thred, lockedCPU, pri);
00370                         _PR_RUNQ_UNLOCK(lockedCPU);
00371                     } else {
00372                         /*
00373                          * The thread was just interrupted and moved
00374                          * from the pause queue to the run queue.
00375                          */
00376                         _PR_THREAD_UNLOCK(thred);
00377                     }
00378                 } else {
00379                     _PR_THREAD_LOCK(thred);
00380                     thred->state = _PR_RUNNABLE;
00381                     _PR_THREAD_UNLOCK(thred);
00382                     ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
00383                 }
00384             }
00385         } else {
00386             PRThread *completed_io;
00387 
00388             PR_ASSERT(mdOlp->ioModel == _MD_BlockingIO);
00389             completed_io = _PR_THREAD_MD_TO_PTR(mdOlp->data.mdThread);
00390             completed_io->md.blocked_io_status = rv;
00391             if (rv == 0)
00392                 completed_io->md.blocked_io_error = GetLastError();
00393             completed_io->md.blocked_io_bytes = bytes;
00394 
00395             if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
00396                 int pri = completed_io->priority;
00397                 _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
00398 
00399                 /* The KEY_CVAR notification only occurs when a native thread
00400                  * is notifying a user thread.  For user-user notifications
00401                  * the wakeup occurs by having the notifier place the thread 
00402                  * on the runq directly; for native-native notifications the
00403                  * wakeup occurs by calling ReleaseSemaphore.
00404                  */
00405                 if ( key == KEY_CVAR ) {
00406                     PR_ASSERT(completed_io->io_pending == PR_FALSE);
00407                     PR_ASSERT(completed_io->io_suspended == PR_FALSE);
00408                     PR_ASSERT(completed_io->md.thr_bound_cpu == NULL);
00409 
00410                     /* Thread has already been deleted from sleepQ */
00411 
00412                     /* Switch CPU and add to runQ */
00413                     completed_io->cpu = lockedCPU;
00414                     completed_io->state = _PR_RUNNABLE;
00415                     _PR_RUNQ_LOCK(lockedCPU);
00416                     _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
00417                     _PR_RUNQ_UNLOCK(lockedCPU);
00418                 } else {
00419                     PR_ASSERT(key == KEY_IO);
00420                     PR_ASSERT(completed_io->io_pending == PR_TRUE);
00421 
00422                     _PR_THREAD_LOCK(completed_io);
00423 
00424                     completed_io->io_pending = PR_FALSE;
00425 
00426                     /* If io_suspended is true, then this IO has already resumed.
00427                      * We don't need to do anything; because the thread is
00428                      * already running.
00429                      */
00430                     if (completed_io->io_suspended == PR_FALSE) {
00431                         if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
00432                             _PR_SLEEPQ_LOCK(completed_io->cpu);
00433                             _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
00434                             _PR_SLEEPQ_UNLOCK(completed_io->cpu);
00435 
00436                             _PR_THREAD_UNLOCK(completed_io);
00437 
00438                                           /*
00439                                            * If an I/O operation is suspended, the thread
00440                                            * must be running on the same cpu on which the
00441                                            * I/O operation was issued.
00442                                            */
00443                                           PR_ASSERT(!completed_io->md.thr_bound_cpu ||
00444                                    (completed_io->cpu == completed_io->md.thr_bound_cpu));
00445 
00446                                                  if (!completed_io->md.thr_bound_cpu)
00447                                    completed_io->cpu = lockedCPU;
00448                             completed_io->state = _PR_RUNNABLE;
00449                             _PR_RUNQ_LOCK(completed_io->cpu);
00450                             _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri);
00451                             _PR_RUNQ_UNLOCK(completed_io->cpu);
00452                         } else {
00453                             _PR_THREAD_UNLOCK(completed_io);
00454                         }
00455                     } else {
00456                         _PR_THREAD_UNLOCK(completed_io);
00457                     }
00458                 }
00459             } else {
00460                 /* For native threads, they are only notified through this loop
00461                  * when completing IO.  So, don't worry about this being a CVAR
00462                  * notification, because that is not possible.
00463                  */
00464                 _PR_THREAD_LOCK(completed_io);
00465                 completed_io->io_pending = PR_FALSE;
00466                 if (completed_io->io_suspended == PR_FALSE) {
00467                     completed_io->state = _PR_RUNNABLE;
00468                     _PR_THREAD_UNLOCK(completed_io);
00469                     rv = ReleaseSemaphore(completed_io->md.blocked_sema,
00470                             1, NULL);
00471                     PR_ASSERT(0 != rv);
00472                 } else {
00473                     _PR_THREAD_UNLOCK(completed_io);
00474                 }
00475             }
00476         }
00477 
00478         awoken++;
00479         timeout = 0;   /* Don't block on subsequent trips through the loop */
00480     }
00481 
00482     /* never reached */
00483     return 0;
00484 }
00485 
00486 static PRStatus
00487 _native_thread_md_wait(PRThread *thread, PRIntervalTime ticks)
00488 {
00489     DWORD rv;
00490        PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
00491               INFINITE : PR_IntervalToMilliseconds(ticks);
00492 
00493        /*
00494         * thread waiting for a cvar or a joining thread
00495         */
00496        rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
00497        switch(rv) {
00498               case WAIT_OBJECT_0:
00499                      return PR_SUCCESS;
00500                      break;
00501               case WAIT_TIMEOUT:
00502                      _PR_THREAD_LOCK(thread);
00503                      PR_ASSERT (thread->state != _PR_IO_WAIT);
00504                      if (thread->wait.cvar != NULL) {
00505                             PR_ASSERT(thread->state == _PR_COND_WAIT);
00506                             thread->wait.cvar = NULL;
00507                             thread->state = _PR_RUNNING;
00508                             _PR_THREAD_UNLOCK(thread);
00509                      } else {
00510                             /* The CVAR was notified just as the timeout
00511                              * occurred.  This left the semaphore in the
00512                              * signaled state.  Call WaitForSingleObject()
00513                              * to clear the semaphore.
00514                              */
00515                             _PR_THREAD_UNLOCK(thread);
00516                             rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
00517                             PR_ASSERT(rv == WAIT_OBJECT_0);
00518                      }
00519                      return PR_SUCCESS;
00520                      break;
00521               default:
00522                      return PR_FAILURE;
00523                      break;
00524        }
00525 
00526     return PR_SUCCESS;
00527 }
00528 
00529 PRStatus
00530 _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
00531 {
00532     DWORD rv;
00533 
00534        if (_native_threads_only) {
00535               return(_native_thread_md_wait(thread, ticks));
00536        }
00537     if ( thread->flags & _PR_GLOBAL_SCOPE ) {
00538         PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
00539             INFINITE : PR_IntervalToMilliseconds(ticks);
00540         rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
00541         switch(rv) {
00542             case WAIT_OBJECT_0:
00543                 return PR_SUCCESS;
00544                 break;
00545             case WAIT_TIMEOUT:
00546                 _PR_THREAD_LOCK(thread);
00547                 if (thread->state == _PR_IO_WAIT) {
00548                     if (thread->io_pending == PR_TRUE) {
00549                         thread->state = _PR_RUNNING;
00550                         thread->io_suspended = PR_TRUE;
00551                         _PR_THREAD_UNLOCK(thread);
00552                     } else {
00553                         /* The IO completed just at the same time the timeout
00554                          * occurred.  This left the semaphore in the signaled
00555                          * state.  Call WaitForSingleObject() to clear the
00556                          * semaphore.
00557                          */
00558                         _PR_THREAD_UNLOCK(thread);
00559                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
00560                         PR_ASSERT(rv == WAIT_OBJECT_0);
00561                     }
00562                 } else {
00563                     if (thread->wait.cvar != NULL) {
00564                         PR_ASSERT(thread->state == _PR_COND_WAIT);
00565                         thread->wait.cvar = NULL;
00566                         thread->state = _PR_RUNNING;
00567                         _PR_THREAD_UNLOCK(thread);
00568                     } else {
00569                         /* The CVAR was notified just as the timeout
00570                          * occurred.  This left the semaphore in the
00571                          * signaled state.  Call WaitForSingleObject()
00572                          * to clear the semaphore.
00573                          */
00574                         _PR_THREAD_UNLOCK(thread);
00575                         rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
00576                         PR_ASSERT(rv == WAIT_OBJECT_0);
00577                     }
00578                 }
00579                 return PR_SUCCESS;
00580                 break;
00581             default:
00582                 return PR_FAILURE;
00583                 break;
00584         }
00585     } else {
00586         PRInt32 is;
00587 
00588         _PR_INTSOFF(is);
00589         _PR_MD_SWITCH_CONTEXT(thread);
00590     }
00591 
00592     return PR_SUCCESS;
00593 }
00594 
00595 static void
00596 _native_thread_io_nowait(
00597     PRThread *thread,
00598     int rv,
00599     int bytes)
00600 {
00601     int rc;
00602 
00603     PR_ASSERT(rv != 0);
00604     _PR_THREAD_LOCK(thread);
00605     if (thread->state == _PR_IO_WAIT) {
00606         PR_ASSERT(thread->io_suspended == PR_FALSE);
00607         PR_ASSERT(thread->io_pending == PR_TRUE);
00608         thread->state = _PR_RUNNING;
00609         thread->io_pending = PR_FALSE;
00610         _PR_THREAD_UNLOCK(thread);
00611     } else {
00612         /* The IO completed just at the same time the
00613          * thread was interrupted. This left the semaphore
00614          * in the signaled state. Call WaitForSingleObject()
00615          * to clear the semaphore.
00616          */
00617         PR_ASSERT(thread->io_suspended == PR_TRUE);
00618         PR_ASSERT(thread->io_pending == PR_TRUE);
00619         thread->io_pending = PR_FALSE;
00620         _PR_THREAD_UNLOCK(thread);
00621         rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
00622         PR_ASSERT(rc == WAIT_OBJECT_0);
00623     }
00624 
00625     thread->md.blocked_io_status = rv;
00626     thread->md.blocked_io_bytes = bytes;
00627     rc = ResetEvent(thread->md.thr_event);
00628     PR_ASSERT(rc != 0);
00629     return;
00630 }
00631 
00632 static PRStatus
00633 _native_thread_io_wait(PRThread *thread, PRIntervalTime ticks)
00634 {
00635     DWORD rv, bytes;
00636 #define _NATIVE_IO_WAIT_HANDLES           2
00637 #define _NATIVE_WAKEUP_EVENT_INDEX 0
00638 #define _NATIVE_IO_EVENT_INDEX            1
00639 
00640        HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES];
00641 
00642        PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
00643               INFINITE : PR_IntervalToMilliseconds(ticks);
00644 
00645     PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE);
00646 
00647        wait_handles[0] = thread->md.blocked_sema;
00648        wait_handles[1] = thread->md.thr_event;
00649        rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles,
00650                                                                       FALSE, msecs);
00651 
00652        switch(rv) {
00653               case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX:
00654                      /*
00655                       * I/O op completed
00656                       */
00657                      _PR_THREAD_LOCK(thread);
00658                      if (thread->state == _PR_IO_WAIT) {
00659 
00660                             PR_ASSERT(thread->io_suspended == PR_FALSE);
00661                             PR_ASSERT(thread->io_pending == PR_TRUE);
00662                             thread->state = _PR_RUNNING;
00663                             thread->io_pending = PR_FALSE;
00664                             _PR_THREAD_UNLOCK(thread);
00665                      } else {
00666                             /* The IO completed just at the same time the
00667                              * thread was interrupted. This led to us being
00668                              * notified twice. Call WaitForSingleObject()
00669                              * to clear the semaphore.
00670                              */
00671                             PR_ASSERT(thread->io_suspended == PR_TRUE);
00672                             PR_ASSERT(thread->io_pending == PR_TRUE);
00673                             thread->io_pending = PR_FALSE;
00674                             _PR_THREAD_UNLOCK(thread);
00675                             rv = WaitForSingleObject(thread->md.blocked_sema,
00676                                                  INFINITE);
00677                             PR_ASSERT(rv == WAIT_OBJECT_0);
00678                      }
00679 
00680                      rv = GetOverlappedResult((HANDLE) thread->io_fd,
00681                             &thread->md.overlapped.overlapped, &bytes, FALSE);
00682 
00683                      thread->md.blocked_io_status = rv;
00684                      if (rv != 0) {
00685                             thread->md.blocked_io_bytes = bytes;
00686                      } else {
00687                             thread->md.blocked_io_error = GetLastError();
00688                             PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error);
00689                      }
00690                      rv = ResetEvent(thread->md.thr_event);
00691                      PR_ASSERT(rv != 0);
00692                      break;
00693               case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX:
00694                      /*
00695                       * I/O interrupted; 
00696                       */
00697 #ifdef DEBUG
00698                      _PR_THREAD_LOCK(thread);
00699                      PR_ASSERT(thread->io_suspended == PR_TRUE);
00700                      _PR_THREAD_UNLOCK(thread);
00701 #endif
00702                      break;
00703               case WAIT_TIMEOUT:
00704                      _PR_THREAD_LOCK(thread);
00705                      if (thread->state == _PR_IO_WAIT) {
00706                             thread->state = _PR_RUNNING;
00707                             thread->io_suspended = PR_TRUE;
00708                             _PR_THREAD_UNLOCK(thread);
00709                      } else {
00710                             /*
00711                              * The thread was interrupted just as the timeout
00712                              * occurred. This left the semaphore in the signaled
00713                              * state. Call WaitForSingleObject() to clear the
00714                              * semaphore.
00715                              */
00716                             PR_ASSERT(thread->io_suspended == PR_TRUE);
00717                             _PR_THREAD_UNLOCK(thread);
00718                             rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
00719                             PR_ASSERT(rv == WAIT_OBJECT_0);
00720                      }
00721                      break;
00722               default:
00723                      return PR_FAILURE;
00724                      break;
00725        }
00726 
00727     return PR_SUCCESS;
00728 }
00729 
00730 
00731 static PRStatus
00732 _NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
00733 {
00734     PRBool fWait = PR_TRUE;
00735 
00736        if (_native_threads_only) {
00737               return(_native_thread_io_wait(thread, timeout));
00738        }
00739     if (!_PR_IS_NATIVE_THREAD(thread))  {
00740 
00741         _PR_THREAD_LOCK(thread);
00742 
00743         /* The IO may have already completed; if so, don't add to sleepQ, 
00744          * since we are already on the runQ!
00745          */
00746         if (thread->io_pending == PR_TRUE) {
00747             _PR_SLEEPQ_LOCK(thread->cpu);
00748             _PR_ADD_SLEEPQ(thread, timeout);
00749             _PR_SLEEPQ_UNLOCK(thread->cpu);
00750         } else
00751             fWait = PR_FALSE;
00752         _PR_THREAD_UNLOCK(thread);
00753     }
00754     if (fWait)
00755         return _PR_MD_WAIT(thread, timeout);
00756     else
00757         return PR_SUCCESS;
00758 }
00759 
00760 /*
00761  * Unblock threads waiting for I/O
00762  * used when interrupting threads
00763  *
00764  * NOTE: The thread lock should held when this function is called.
00765  * On return, the thread lock is released.
00766  */
00767 void _PR_Unblock_IO_Wait(PRThread *thr)
00768 {
00769     PRStatus rv;
00770     _PRCPU *cpu = thr->cpu;
00771  
00772     PR_ASSERT(thr->state == _PR_IO_WAIT);
00773        /*
00774         * A thread for which an I/O timed out or was interrupted cannot be
00775         * in an IO_WAIT state except as a result of calling PR_Close or
00776         * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state
00777         * is not interruptible
00778         */
00779        if (thr->md.interrupt_disabled == PR_TRUE) {
00780        _PR_THREAD_UNLOCK(thr);
00781               return;
00782        }
00783     thr->io_suspended = PR_TRUE;
00784     thr->state = _PR_RUNNABLE;
00785 
00786     if (!_PR_IS_NATIVE_THREAD(thr)) {
00787         PRThread *me = _PR_MD_CURRENT_THREAD();
00788         PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
00789         _PR_SLEEPQ_LOCK(cpu);
00790         _PR_DEL_SLEEPQ(thr, PR_TRUE);
00791         _PR_SLEEPQ_UNLOCK(cpu);
00792               /*
00793                * this thread will continue to run on the same cpu until the
00794                * I/O is aborted by closing the FD or calling CancelIO
00795                */
00796               thr->md.thr_bound_cpu = cpu;
00797 
00798         PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
00799         _PR_AddThreadToRunQ(me, thr);
00800     }
00801     _PR_THREAD_UNLOCK(thr);
00802     rv = _PR_MD_WAKEUP_WAITER(thr);
00803     PR_ASSERT(PR_SUCCESS == rv);
00804 }
00805 
00806 /* Resume an outstanding IO; requires that after the switch, we disable */
00807 static PRStatus
00808 _NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
00809 {
00810     PRBool fWait = PR_TRUE;
00811 
00812     if (!_PR_IS_NATIVE_THREAD(thread)) {
00813         if (_pr_use_static_tls) {
00814             _pr_io_restarted_io = thread;
00815         } else {
00816             TlsSetValue(_pr_io_restartedIOIndex, thread);
00817         }
00818     } else {
00819         _PR_THREAD_LOCK(thread);
00820         if (!thread->io_pending)
00821             fWait = PR_FALSE;
00822         thread->io_suspended = PR_FALSE;
00823             
00824         _PR_THREAD_UNLOCK(thread);
00825     }
00826     /* We don't put ourselves back on the sleepQ yet; until we 
00827      * set the suspended bit to false, we can't do that.  Just save
00828      * the sleep time here, and then continue.  The restarted_io handler
00829      * will add us to the sleepQ if needed.
00830      */
00831     thread->sleep = ticks;
00832 
00833     if (fWait) {
00834         if (!_PR_IS_NATIVE_THREAD(thread))
00835             return _PR_MD_WAIT(thread, ticks);
00836         else
00837             return _NT_IO_WAIT(thread, ticks);
00838     }
00839     return PR_SUCCESS;
00840 }
00841 
00842 PRStatus
00843 _PR_MD_WAKEUP_WAITER(PRThread *thread)
00844 {
00845     if (thread == NULL) {
00846         /* If thread is NULL, we aren't waking a thread, we're just poking
00847          * idle thread 
00848          */
00849         if ( PostQueuedCompletionStatus(_pr_completion_port, 0, 
00850             KEY_CVAR, NULL) == FALSE) 
00851             return PR_FAILURE;
00852         return PR_SUCCESS;
00853     }
00854 
00855     if ( _PR_IS_NATIVE_THREAD(thread) ) {
00856         if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
00857             return PR_FAILURE;
00858         else
00859             return PR_SUCCESS;
00860     } else {
00861         PRThread *me = _PR_MD_CURRENT_THREAD();
00862 
00863         /* When a Native thread has to awaken a user thread, it has to poke
00864          * the completion port because all user threads might be idle, and
00865          * thus the CPUs are just waiting for a completion.  
00866          *
00867          * XXXMB - can we know when we are truely idle (and not checking 
00868          *         the runq)?
00869          */
00870         if ((_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) &&
00871                 (!thread->md.thr_bound_cpu)) {
00872             /* The thread should not be in any queue */
00873             PR_ASSERT(thread->queueCount == 0);
00874             if ( PostQueuedCompletionStatus(_pr_completion_port, 0, 
00875                 KEY_CVAR, &(thread->md.overlapped.overlapped)) == FALSE) 
00876                 return PR_FAILURE;
00877         }
00878         return PR_SUCCESS;
00879     }
00880 }
00881 
00882 void
00883 _PR_MD_INIT_IO()
00884 {
00885     WORD WSAVersion = 0x0101;
00886     WSADATA WSAData;
00887     int err;
00888     OSVERSIONINFO OSversion;
00889 
00890     err = WSAStartup( WSAVersion, &WSAData );
00891     PR_ASSERT(0 == err);
00892                                                       
00893     _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 
00894                                                  NULL, 
00895                                                  0, 
00896                                                  0);
00897  
00898     _MD_NEW_LOCK(&_pr_recycle_lock);
00899     _MD_NEW_LOCK(&_pr_ioq_lock);
00900 
00901     OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00902     if (GetVersionEx(&OSversion)) {
00903         _nt_version_gets_lockfile_completion = PR_FALSE;
00904         if (OSversion.dwMajorVersion >= 4) {
00905             _nt_version_gets_lockfile_completion = PR_TRUE;
00906         }
00907     } else 
00908         PR_ASSERT(0);
00909 
00910     IsFileLocalInit();
00911 
00912     /*
00913      * UDP support: start up the continuation thread
00914      */
00915 
00916     pt_tq.op_count = 0;
00917     pt_tq.head = pt_tq.tail = NULL;
00918     pt_tq.ml = PR_NewLock();
00919     PR_ASSERT(NULL != pt_tq.ml);
00920     pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
00921     PR_ASSERT(NULL != pt_tq.new_op);
00922 #if defined(DEBUG)
00923     memset(&pt_debug, 0, sizeof(struct pt_debug_s));
00924 #endif
00925 
00926     pt_tq.thread = PR_CreateThread(
00927         PR_SYSTEM_THREAD, ContinuationThread, NULL,
00928         PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
00929 
00930     PR_ASSERT(NULL != pt_tq.thread);
00931 
00932 #ifdef DEBUG
00933     /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
00934     {
00935         SYSTEMTIME systime;
00936         union {
00937            PRTime prt;
00938            FILETIME ft;
00939         } filetime;
00940         BOOL rv;
00941 
00942         systime.wYear = 1970;
00943         systime.wMonth = 1;
00944         /* wDayOfWeek is ignored */
00945         systime.wDay = 1;
00946         systime.wHour = 0;
00947         systime.wMinute = 0;
00948         systime.wSecond = 0;
00949         systime.wMilliseconds = 0;
00950 
00951         rv = SystemTimeToFileTime(&systime, &filetime.ft);
00952         PR_ASSERT(0 != rv);
00953         PR_ASSERT(filetime.prt == _pr_filetime_offset);
00954     }
00955 #endif /* DEBUG */
00956 
00957     _PR_NT_InitSids();
00958 }
00959 
00960 /* --- SOCKET IO --------------------------------------------------------- */
00961 
00962 /* _md_get_recycled_socket()
00963  * Get a socket from the recycle bin; if no sockets are in the bin,
00964  * create one.  The socket will be passed to AcceptEx() as the
00965  * second argument.
00966  */
00967 static SOCKET
00968 _md_get_recycled_socket(int af)
00969 {
00970     SOCKET rv;
00971 
00972     _MD_LOCK(&_pr_recycle_lock);
00973     if (af == AF_INET && _pr_recycle_INET_tail) {
00974         _pr_recycle_INET_tail--;
00975         rv = _pr_recycle_INET_array[_pr_recycle_INET_tail];
00976         _MD_UNLOCK(&_pr_recycle_lock);
00977         return rv;
00978     }
00979     if (af == AF_INET6 && _pr_recycle_INET6_tail) {
00980         _pr_recycle_INET6_tail--;
00981         rv = _pr_recycle_INET6_array[_pr_recycle_INET6_tail];
00982         _MD_UNLOCK(&_pr_recycle_lock);
00983         return rv;
00984     }
00985     _MD_UNLOCK(&_pr_recycle_lock);
00986 
00987     rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
00988     if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
00989         closesocket(rv);
00990         return INVALID_SOCKET;
00991     }
00992     return rv;
00993 }
00994 
00995 /* _md_put_recycled_socket()
00996  * Add a socket to the recycle bin.
00997  */
00998 static void
00999 _md_put_recycled_socket(SOCKET newsock, int af)
01000 {
01001     PR_ASSERT(_pr_recycle_INET_tail >= 0);
01002     PR_ASSERT(_pr_recycle_INET6_tail >= 0);
01003 
01004     _MD_LOCK(&_pr_recycle_lock);
01005     if (af == AF_INET && _pr_recycle_INET_tail < RECYCLE_SIZE) {
01006         _pr_recycle_INET_array[_pr_recycle_INET_tail] = newsock;
01007         _pr_recycle_INET_tail++;
01008         _MD_UNLOCK(&_pr_recycle_lock);
01009     } else if (af == AF_INET6 && _pr_recycle_INET6_tail < RECYCLE_SIZE) {
01010         _pr_recycle_INET6_array[_pr_recycle_INET6_tail] = newsock;
01011         _pr_recycle_INET6_tail++;
01012         _MD_UNLOCK(&_pr_recycle_lock);
01013     } else {
01014         _MD_UNLOCK(&_pr_recycle_lock);
01015         closesocket(newsock);
01016     }
01017  
01018     return;
01019 }
01020 
01021 /* _md_Associate()
01022  * Associates a file with the completion port.
01023  * Returns 0 on failure, 1 on success.
01024  */
01025 PRInt32
01026 _md_Associate(HANDLE file)
01027 {
01028     HANDLE port;
01029 
01030        if (!_native_threads_only) {
01031               port = CreateIoCompletionPort((HANDLE)file, 
01032                                                                       _pr_completion_port, 
01033                                                                       KEY_IO,
01034                                                                       0);
01035 
01036               /* XXX should map error codes on failures */
01037               return (port == _pr_completion_port);
01038        } else {
01039               return 1;
01040        }
01041 }
01042 
01043 /*
01044  * _md_MakeNonblock()
01045  * Make a socket nonblocking.
01046  * Returns 0 on failure, 1 on success.
01047  */
01048 static PRInt32
01049 _md_MakeNonblock(HANDLE file)
01050 {
01051     int rv;
01052     u_long one = 1;
01053 
01054     rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
01055     /* XXX should map error codes on failures */
01056     return (rv == 0);
01057 }
01058 
01059 static int missing_completions = 0;
01060 static int max_wait_loops = 0;
01061 
01062 static PRInt32
01063 _NT_IO_ABORT(PRInt32 sock)
01064 {
01065     PRThread *me = _PR_MD_CURRENT_THREAD();
01066     PRBool fWait;
01067     PRInt32 rv;
01068     int loop_count;
01069 
01070     /* This is a clumsy way to abort the IO, but it is all we can do.
01071      * It looks a bit racy, but we handle all the cases. 
01072      * case 1:  IO completes before calling closesocket
01073      *     case 1a:  fWait is set to PR_FALSE
01074      *           This should e the most likely case.  We'll properly
01075      *           not wait call _NT_IO_WAIT, since the closesocket()
01076      *           won't be forcing a completion.
01077      *     case 1b: fWait is set to PR_TRUE
01078      *           This hopefully won't happen much.  When it does, this
01079      *           thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
01080      *           before cleaning up.
01081      * case 2:  IO does not complete before calling closesocket
01082      *     case 2a: IO never completes
01083      *           This is the likely case.  We'll close it and wait
01084      *           for the completion forced by the close.  Return should
01085      *           be immediate.
01086      *     case 2b: IO completes just after calling closesocket
01087      *           Since the closesocket is issued, we'll either get a
01088      *           completion back for the real IO or for the close.  We
01089      *           don't really care.  It may not even be possible to get
01090      *           a real completion here.  In any event, we'll awaken
01091      *           from NT_IO_WAIT immediately.
01092      */
01093 
01094     _PR_THREAD_LOCK(me);
01095     fWait = me->io_pending;
01096     if (fWait) {
01097         /*
01098          * If there's still I/O pending, it should have already timed
01099          * out once before this function is called.
01100          */
01101         PR_ASSERT(me->io_suspended == PR_TRUE);
01102 
01103         /* Set up to wait for I/O completion again */
01104         me->state = _PR_IO_WAIT;
01105         me->io_suspended = PR_FALSE;
01106         me->md.interrupt_disabled = PR_TRUE;
01107     }
01108     _PR_THREAD_UNLOCK(me);
01109 
01110     /* Close the socket if there is one */
01111     if (sock != INVALID_SOCKET) {
01112         rv = closesocket((SOCKET)sock);
01113     }
01114 
01115     /* If there was I/O pending before the close, wait for it to complete */
01116     if (fWait) {
01117 
01118         /* Wait and wait for the I/O to complete */
01119         for (loop_count = 0; fWait; ++loop_count) {
01120 
01121             _NT_IO_WAIT(me, CLOSE_TIMEOUT);
01122 
01123             _PR_THREAD_LOCK(me);
01124             fWait = me->io_pending;
01125             if (fWait) {
01126                 PR_ASSERT(me->io_suspended == PR_TRUE);
01127                 me->state = _PR_IO_WAIT;
01128                 me->io_suspended = PR_FALSE;
01129             }
01130             _PR_THREAD_UNLOCK(me);
01131 
01132             if (loop_count > max_wait_loops) {
01133                 max_wait_loops = loop_count;
01134             }
01135         }
01136 
01137         if (loop_count > 1) {
01138             ++missing_completions;
01139         }
01140 
01141         me->md.interrupt_disabled = PR_FALSE;
01142         me->io_pending = PR_FALSE;
01143         me->state = _PR_RUNNING;
01144     }
01145 
01146     PR_ASSERT(me->io_pending == PR_FALSE);
01147     me->md.thr_bound_cpu = NULL;
01148     me->io_suspended = PR_FALSE;
01149 
01150     return rv;
01151 }
01152 
01153 
01154 PRInt32
01155 _PR_MD_SOCKET(int af, int type, int flags)
01156 {
01157     SOCKET sock;
01158 
01159     sock = socket(af, type, flags);
01160 
01161     if (sock == INVALID_SOCKET) {
01162         _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
01163     }
01164 
01165     return (PRInt32)sock;
01166 }
01167 
01168 struct connect_data_s {
01169     PRInt32 status;
01170     PRInt32 error;
01171     PRInt32 osfd;
01172     struct sockaddr *addr;
01173     PRUint32 addrlen;
01174     PRIntervalTime timeout;
01175 };
01176 
01177 void
01178 _PR_MD_connect_thread(void *cdata)
01179 {
01180     struct connect_data_s *cd = (struct connect_data_s *)cdata;
01181 
01182     cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
01183 
01184     if (cd->status == SOCKET_ERROR)
01185         cd->error = WSAGetLastError();
01186 
01187     return;
01188 }
01189 
01190 
01191 PRInt32
01192 _PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, 
01193                PRIntervalTime timeout)
01194 {
01195     PRInt32 osfd = fd->secret->md.osfd;
01196     PRInt32 rv, err;
01197     u_long nbio;
01198     PRInt32 rc;
01199 
01200     if (fd->secret->nonblocking) {
01201         if (!fd->secret->md.io_model_committed) {
01202             rv = _md_MakeNonblock((HANDLE)osfd);
01203             PR_ASSERT(0 != rv);
01204             fd->secret->md.io_model_committed = PR_TRUE;
01205         }
01206 
01207         if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
01208             err = WSAGetLastError();
01209             _PR_MD_MAP_CONNECT_ERROR(err);
01210         }
01211         return rv;
01212     }
01213 
01214     /*
01215      * Temporarily make the socket non-blocking so that we can
01216      * initiate a non-blocking connect and wait for its completion
01217      * (with a timeout) in select.
01218      */
01219     PR_ASSERT(!fd->secret->md.io_model_committed);
01220     nbio = 1;
01221     rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
01222     PR_ASSERT(0 == rv);
01223 
01224     rc = _nt_nonblock_connect(fd, (struct sockaddr *) addr, addrlen, timeout);
01225 
01226     /* Set the socket back to blocking. */
01227     nbio = 0;
01228     rv = ioctlsocket((SOCKET)osfd, FIONBIO, &nbio);
01229     PR_ASSERT(0 == rv);
01230 
01231     return rc;
01232 }
01233 
01234 PRInt32
01235 _PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
01236 {
01237     PRInt32 rv;
01238 #if 0
01239     int one = 1;
01240 #endif
01241 
01242     rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
01243 
01244     if (rv == SOCKET_ERROR) {
01245         _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
01246         return -1;
01247     }
01248 
01249 #if 0
01250     /* Disable nagle- so far unknown if this is good or not...
01251      */
01252     rv = setsockopt(fd->secret->md.osfd, 
01253                     SOL_SOCKET,
01254                     TCP_NODELAY,
01255                     (const char *)&one,
01256                     sizeof(one));
01257     PR_ASSERT(rv == 0);
01258 #endif
01259 
01260     return 0;
01261 }
01262 
01263 void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 accept_sock, PRInt32 listen_sock)
01264 {
01265     /* Sockets accept()'d with AcceptEx need to call this setsockopt before
01266      * calling anything other than ReadFile(), WriteFile(), send(), recv(), 
01267      * Transmitfile(), and closesocket().  In order to call any other 
01268      * winsock functions, we have to make this setsockopt call.
01269      *
01270      * XXXMB - For the server, we *NEVER* need this in
01271      * the "normal" code path.  But now we have to call it.  This is a waste
01272      * of a system call.  We'd like to only call it before calling the 
01273      * obscure socket calls, but since we don't know at that point what the
01274      * original socket was (or even if it is still alive) we can't do it
01275      * at that point... 
01276      */
01277     setsockopt((SOCKET)accept_sock, 
01278                SOL_SOCKET, 
01279                SO_UPDATE_ACCEPT_CONTEXT,
01280                (char *)&listen_sock,
01281                sizeof(listen_sock));
01282 
01283 }
01284 
01285 #define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
01286 PRInt32
01287 _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
01288               PRIntervalTime timeout, PRBool fast, 
01289               _PR_AcceptTimeoutCallback callback, void *callbackArg)
01290 {
01291     PRInt32 osfd = fd->secret->md.osfd;
01292     PRThread *me = _PR_MD_CURRENT_THREAD();
01293     SOCKET accept_sock;
01294     int bytes;
01295     PRNetAddr *Laddr;
01296     PRNetAddr *Raddr;
01297     PRUint32 llen, err;
01298     int rv;
01299 
01300     if (_NT_USE_NB_IO(fd)) {
01301         if (!fd->secret->md.io_model_committed) {
01302             rv = _md_MakeNonblock((HANDLE)osfd);
01303             PR_ASSERT(0 != rv);
01304             fd->secret->md.io_model_committed = PR_TRUE;
01305         }
01306         /*
01307          * The accepted socket inherits the nonblocking and
01308          * inheritable (HANDLE_FLAG_INHERIT) attributes of
01309          * the listening socket.
01310          */
01311         accept_sock = _nt_nonblock_accept(fd, (struct sockaddr *)raddr, rlen, timeout);
01312         if (!fd->secret->nonblocking) {
01313             u_long zero = 0;
01314 
01315             rv = ioctlsocket(accept_sock, FIONBIO, &zero);
01316             PR_ASSERT(0 == rv);
01317         }
01318         return accept_sock;
01319     }
01320 
01321     if (me->io_suspended) {
01322         PR_SetError(PR_INVALID_STATE_ERROR, 0);
01323         return -1;
01324     }
01325 
01326     if (!fd->secret->md.io_model_committed) {
01327         rv = _md_Associate((HANDLE)osfd);
01328         PR_ASSERT(0 != rv);
01329         fd->secret->md.io_model_committed = PR_TRUE;
01330     }
01331 
01332     if (!me->md.acceptex_buf) {
01333         me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
01334         if (!me->md.acceptex_buf) {
01335             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
01336             return -1;
01337         }
01338     }
01339 
01340     accept_sock = _md_get_recycled_socket(fd->secret->af);
01341     if (accept_sock == INVALID_SOCKET)
01342         return -1;
01343 
01344     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
01345        if (_native_threads_only)
01346               me->md.overlapped.overlapped.hEvent = me->md.thr_event;
01347 
01348     _PR_THREAD_LOCK(me);
01349        if (_PR_PENDING_INTERRUPT(me)) {
01350               me->flags &= ~_PR_INTERRUPT;
01351               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01352        _PR_THREAD_UNLOCK(me);
01353               closesocket(accept_sock);
01354               return -1;
01355        }
01356     me->io_pending = PR_TRUE;
01357     me->state = _PR_IO_WAIT;
01358     _PR_THREAD_UNLOCK(me);
01359     me->io_fd = osfd;
01360 
01361     rv = AcceptEx((SOCKET)osfd,
01362                   accept_sock,
01363                   me->md.acceptex_buf,
01364                   0,
01365                   INET_ADDR_PADDED,
01366                   INET_ADDR_PADDED,
01367                   &bytes,
01368                   &(me->md.overlapped.overlapped));
01369 
01370     if ( (rv == 0) && ((err = WSAGetLastError()) != ERROR_IO_PENDING))  {
01371         /* Argh! The IO failed */
01372               closesocket(accept_sock);
01373               _PR_THREAD_LOCK(me);
01374               me->io_pending = PR_FALSE;
01375               me->state = _PR_RUNNING;
01376               if (_PR_PENDING_INTERRUPT(me)) {
01377                      me->flags &= ~_PR_INTERRUPT;
01378                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01379                      _PR_THREAD_UNLOCK(me);
01380                      return -1;
01381               }
01382               _PR_THREAD_UNLOCK(me);
01383 
01384               _PR_MD_MAP_ACCEPTEX_ERROR(err);
01385         return -1;
01386     }
01387 
01388     if (_native_threads_only && rv) {
01389         _native_thread_io_nowait(me, rv, bytes);
01390     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
01391         PR_ASSERT(0);
01392         closesocket(accept_sock);
01393         return -1;
01394     }
01395 
01396     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
01397 
01398     if (me->io_suspended) {
01399         closesocket(accept_sock);
01400         if (_PR_PENDING_INTERRUPT(me)) {
01401             me->flags &= ~_PR_INTERRUPT;
01402             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01403         } else {
01404             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
01405         }
01406         return -1;
01407     }
01408 
01409     if (me->md.blocked_io_status == 0) {
01410               closesocket(accept_sock);
01411               _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
01412         return -1;
01413     }
01414 
01415     if (!fast)
01416         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
01417 
01418     /* IO is done */
01419     GetAcceptExSockaddrs(
01420             me->md.acceptex_buf,
01421             0,
01422             INET_ADDR_PADDED,
01423             INET_ADDR_PADDED,
01424             (LPSOCKADDR *)&(Laddr),
01425             &llen,
01426             (LPSOCKADDR *)&(Raddr),
01427             (unsigned int *)rlen);
01428 
01429     if (raddr != NULL)
01430         memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
01431 
01432     PR_ASSERT(me->io_pending == PR_FALSE);
01433 
01434     return accept_sock;
01435 }
01436 
01437 PRInt32
01438 _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, 
01439                    void *buf, PRInt32 amount, PRIntervalTime timeout, 
01440                    PRBool fast, _PR_AcceptTimeoutCallback callback, 
01441                    void *callbackArg)
01442 {
01443     PRInt32 sock = sd->secret->md.osfd;
01444     PRThread *me = _PR_MD_CURRENT_THREAD();
01445     int bytes;
01446     PRNetAddr *Laddr;
01447     PRUint32 llen, rlen, err;
01448     int rv;
01449     PRBool isConnected;
01450     PRBool madeCallback = PR_FALSE;
01451 
01452     if (me->io_suspended) {
01453         PR_SetError(PR_INVALID_STATE_ERROR, 0);
01454         return -1;
01455     }
01456 
01457     if (!sd->secret->md.io_model_committed) {
01458         rv = _md_Associate((HANDLE)sock);
01459         PR_ASSERT(0 != rv);
01460         sd->secret->md.io_model_committed = PR_TRUE;
01461     }
01462 
01463     *newSock = _md_get_recycled_socket(sd->secret->af);
01464     if (*newSock == INVALID_SOCKET)
01465         return -1;
01466 
01467     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
01468        if (_native_threads_only)
01469               me->md.overlapped.overlapped.hEvent = me->md.thr_event;
01470 
01471     _PR_THREAD_LOCK(me);
01472        if (_PR_PENDING_INTERRUPT(me)) {
01473               me->flags &= ~_PR_INTERRUPT;
01474               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01475        _PR_THREAD_UNLOCK(me);
01476               closesocket(*newSock);
01477               return -1;
01478        }
01479     me->io_pending = PR_TRUE;
01480     me->state = _PR_IO_WAIT;
01481     _PR_THREAD_UNLOCK(me);
01482     me->io_fd = sock;
01483 
01484     rv = AcceptEx((SOCKET)sock,
01485                   *newSock,
01486                   buf,
01487                   amount,
01488                   INET_ADDR_PADDED,
01489                   INET_ADDR_PADDED,
01490                   &bytes,
01491                   &(me->md.overlapped.overlapped));
01492 
01493     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
01494               closesocket(*newSock);
01495               _PR_THREAD_LOCK(me);
01496               me->io_pending = PR_FALSE;
01497               me->state = _PR_RUNNING;
01498               if (_PR_PENDING_INTERRUPT(me)) {
01499                      me->flags &= ~_PR_INTERRUPT;
01500                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01501                      _PR_THREAD_UNLOCK(me);
01502                      return -1;
01503               }
01504               _PR_THREAD_UNLOCK(me);
01505 
01506               _PR_MD_MAP_ACCEPTEX_ERROR(err);
01507         return -1;
01508     }
01509 
01510     if (_native_threads_only && rv) {
01511         _native_thread_io_nowait(me, rv, bytes);
01512     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
01513         PR_ASSERT(0);
01514         closesocket(*newSock);
01515         return -1;
01516     }
01517 
01518 retry:
01519     if (me->io_suspended) {
01520         PRInt32 err;
01521         INT seconds;
01522         INT bytes = sizeof(seconds);
01523 
01524         PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
01525 
01526         err = getsockopt(*newSock, 
01527                          SOL_SOCKET,
01528                          SO_CONNECT_TIME,
01529                          (char *)&seconds,
01530                          (PINT)&bytes);
01531         if ( err == NO_ERROR ) {
01532             PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
01533 
01534             if (seconds == 0xffffffff) 
01535                 isConnected = PR_FALSE;
01536             else 
01537                 isConnected = PR_TRUE;
01538 
01539             if (!isConnected) {
01540                 if (madeCallback == PR_FALSE && callback)
01541                     callback(callbackArg);
01542                 madeCallback = PR_TRUE;
01543                 me->state = _PR_IO_WAIT;
01544                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
01545                     closesocket(*newSock);
01546                     return -1;
01547                 }
01548                 goto retry;
01549             }
01550 
01551             if (elapsed < timeout) {
01552                 /* Socket is connected but time not elapsed, RESUME IO */
01553                 timeout -= elapsed;
01554                 me->state = _PR_IO_WAIT;
01555                 if (_NT_ResumeIO(me, timeout) == PR_FAILURE) {
01556                     closesocket(*newSock);
01557                     return -1;
01558                 }
01559                 goto retry;
01560             }
01561         } else {
01562             /*  What to do here? Assume socket not open?*/
01563             PR_ASSERT(0);
01564             isConnected = PR_FALSE;
01565         }
01566 
01567         rv = _NT_IO_ABORT(*newSock);
01568 
01569         PR_ASSERT(me->io_pending ==  PR_FALSE);
01570         PR_ASSERT(me->io_suspended ==  PR_FALSE);
01571         PR_ASSERT(me->md.thr_bound_cpu ==  NULL);
01572         /* If the IO is still suspended, it means we didn't get any 
01573          * completion from NT_IO_WAIT.  This is not disasterous, I hope,
01574          * but it may mean we still have an IO outstanding...  Try to 
01575          * recover by just allowing ourselves to continue.
01576          */
01577         me->io_suspended = PR_FALSE;
01578         if (_PR_PENDING_INTERRUPT(me)) {
01579             me->flags &= ~_PR_INTERRUPT;
01580             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01581         } else {
01582             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
01583         }
01584         me->state = _PR_RUNNING;
01585         closesocket(*newSock);
01586         return -1;
01587     }
01588 
01589     PR_ASSERT(me->io_pending == PR_FALSE);
01590     PR_ASSERT(me->io_suspended == PR_FALSE);
01591     PR_ASSERT(me->md.thr_bound_cpu == NULL);
01592 
01593     if (me->md.blocked_io_status == 0) {
01594               _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
01595         closesocket(*newSock);
01596         return -1;
01597     }
01598 
01599     if (!fast) 
01600         _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
01601 
01602     /* IO is done */
01603     GetAcceptExSockaddrs(
01604             buf,
01605             amount,
01606             INET_ADDR_PADDED,
01607             INET_ADDR_PADDED,
01608             (LPSOCKADDR *)&(Laddr),
01609             &llen,
01610             (LPSOCKADDR *)(raddr),
01611             (unsigned int *)&rlen);
01612 
01613     return me->md.blocked_io_bytes;
01614 }
01615 
01616 PRInt32
01617 _PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd,
01618                                    PRInt32 flags, PRIntervalTime timeout)
01619 {
01620     PRThread *me = _PR_MD_CURRENT_THREAD();
01621     PRInt32 tflags;
01622     int rv, err;
01623 
01624     if (me->io_suspended) {
01625         PR_SetError(PR_INVALID_STATE_ERROR, 0);
01626         return -1;
01627     }
01628 
01629     if (!sock->secret->md.io_model_committed) {
01630         rv = _md_Associate((HANDLE)sock->secret->md.osfd);
01631         PR_ASSERT(0 != rv);
01632         sock->secret->md.io_model_committed = PR_TRUE;
01633     }
01634     if (!me->md.xmit_bufs) {
01635         me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
01636         if (!me->md.xmit_bufs) {
01637             PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
01638             return -1;
01639         }
01640     }
01641     me->md.xmit_bufs->Head       = (void *)sfd->header;
01642     me->md.xmit_bufs->HeadLength = sfd->hlen;
01643     me->md.xmit_bufs->Tail       = (void *)sfd->trailer;
01644     me->md.xmit_bufs->TailLength = sfd->tlen;
01645 
01646     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
01647     me->md.overlapped.overlapped.Offset = sfd->file_offset;
01648        if (_native_threads_only)
01649               me->md.overlapped.overlapped.hEvent = me->md.thr_event;
01650 
01651     tflags = 0;
01652     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
01653         tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
01654 
01655     _PR_THREAD_LOCK(me);
01656        if (_PR_PENDING_INTERRUPT(me)) {
01657               me->flags &= ~_PR_INTERRUPT;
01658               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01659        _PR_THREAD_UNLOCK(me);
01660               return -1;
01661        }
01662     me->io_pending = PR_TRUE;
01663     me->state = _PR_IO_WAIT;
01664     _PR_THREAD_UNLOCK(me);
01665     me->io_fd = sock->secret->md.osfd;
01666 
01667     rv = TransmitFile((SOCKET)sock->secret->md.osfd,
01668                       (HANDLE)sfd->fd->secret->md.osfd,
01669                       (DWORD)sfd->file_nbytes,
01670                       (DWORD)0,
01671                       (LPOVERLAPPED)&(me->md.overlapped.overlapped),
01672                       (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
01673                       (DWORD)tflags);
01674     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
01675               _PR_THREAD_LOCK(me);
01676               me->io_pending = PR_FALSE;
01677               me->state = _PR_RUNNING;
01678               if (_PR_PENDING_INTERRUPT(me)) {
01679                      me->flags &= ~_PR_INTERRUPT;
01680                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01681                      _PR_THREAD_UNLOCK(me);
01682                      return -1;
01683               }
01684               _PR_THREAD_UNLOCK(me);
01685 
01686               _PR_MD_MAP_TRANSMITFILE_ERROR(err);
01687         return -1;
01688     }
01689 
01690     if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
01691         PR_ASSERT(0);
01692         return -1;
01693     }
01694 
01695     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
01696 
01697     if (me->io_suspended) {
01698         if (_PR_PENDING_INTERRUPT(me)) {
01699             me->flags &= ~_PR_INTERRUPT;
01700             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01701         } else {
01702             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
01703         }
01704         return -1;
01705     }
01706 
01707     if (me->md.blocked_io_status == 0) {
01708               _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
01709         return -1;
01710     }
01711 
01712     if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
01713         _md_put_recycled_socket(sock->secret->md.osfd, sock->secret->af);
01714     }
01715 
01716     PR_ASSERT(me->io_pending == PR_FALSE);
01717 
01718     return me->md.blocked_io_bytes;
01719 }
01720 
01721 PRInt32
01722 _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, 
01723             PRIntervalTime timeout)
01724 {
01725     PRInt32 osfd = fd->secret->md.osfd;
01726     PRThread *me = _PR_MD_CURRENT_THREAD();
01727     int bytes;
01728     int rv, err;
01729 
01730     if (_NT_USE_NB_IO(fd)) {
01731         if (!fd->secret->md.io_model_committed) {
01732             rv = _md_MakeNonblock((HANDLE)osfd);
01733             PR_ASSERT(0 != rv);
01734             fd->secret->md.io_model_committed = PR_TRUE;
01735         }
01736         return _nt_nonblock_recv(fd, buf, amount, flags, timeout);
01737     }
01738 
01739     if (me->io_suspended) {
01740         PR_SetError(PR_INVALID_STATE_ERROR, 0);
01741         return -1;
01742     }
01743 
01744     if (!fd->secret->md.io_model_committed) {
01745         rv = _md_Associate((HANDLE)osfd);
01746         PR_ASSERT(0 != rv);
01747         fd->secret->md.io_model_committed = PR_TRUE;
01748     }
01749 
01750     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
01751        if (_native_threads_only)
01752               me->md.overlapped.overlapped.hEvent = me->md.thr_event;
01753 
01754     _PR_THREAD_LOCK(me);
01755        if (_PR_PENDING_INTERRUPT(me)) {
01756               me->flags &= ~_PR_INTERRUPT;
01757               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01758        _PR_THREAD_UNLOCK(me);
01759               return -1;
01760        }
01761     me->io_pending = PR_TRUE;
01762     me->state = _PR_IO_WAIT;
01763     _PR_THREAD_UNLOCK(me);
01764     me->io_fd = osfd;
01765 
01766     rv = ReadFile((HANDLE)osfd,
01767                   buf, 
01768                   amount,
01769                   &bytes,
01770                   &(me->md.overlapped.overlapped));
01771     if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
01772        _PR_THREAD_LOCK(me);
01773         me->io_pending = PR_FALSE;
01774         me->state = _PR_RUNNING;
01775               if (_PR_PENDING_INTERRUPT(me)) {
01776                      me->flags &= ~_PR_INTERRUPT;
01777                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01778                      _PR_THREAD_UNLOCK(me);
01779                      return -1;
01780               }
01781               _PR_THREAD_UNLOCK(me);
01782 
01783         if ((err = GetLastError()) == ERROR_HANDLE_EOF)
01784             return 0;
01785               _PR_MD_MAP_READ_ERROR(err);
01786         return -1;
01787     }
01788 
01789     if (_native_threads_only && rv) {
01790         _native_thread_io_nowait(me, rv, bytes);
01791     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
01792         PR_ASSERT(0);
01793         return -1;
01794     }
01795 
01796     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
01797 
01798     if (me->io_suspended) {
01799         if (_PR_PENDING_INTERRUPT(me)) {
01800             me->flags &= ~_PR_INTERRUPT;
01801             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01802         } else {
01803             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
01804         }
01805         return -1;
01806     }
01807 
01808     if (me->md.blocked_io_status == 0) {
01809         if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
01810             return 0;
01811               _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
01812         return -1;
01813     }
01814 
01815     PR_ASSERT(me->io_pending == PR_FALSE);
01816 
01817     return me->md.blocked_io_bytes;
01818 }
01819 
01820 PRInt32
01821 _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
01822             PRIntervalTime timeout)
01823 {
01824     PRInt32 osfd = fd->secret->md.osfd;
01825     PRThread *me = _PR_MD_CURRENT_THREAD();
01826     int bytes;
01827     int rv, err;
01828 
01829     if (_NT_USE_NB_IO(fd)) {
01830         if (!fd->secret->md.io_model_committed) {
01831             rv = _md_MakeNonblock((HANDLE)osfd);
01832             PR_ASSERT(0 != rv);
01833             fd->secret->md.io_model_committed = PR_TRUE;
01834         }
01835         return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
01836     }
01837 
01838     if (me->io_suspended) {
01839         PR_SetError(PR_INVALID_STATE_ERROR, 0);
01840         return -1;
01841     }
01842 
01843     if (!fd->secret->md.io_model_committed) {
01844         rv = _md_Associate((HANDLE)osfd);
01845         PR_ASSERT(0 != rv);
01846         fd->secret->md.io_model_committed = PR_TRUE;
01847     }
01848 
01849     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
01850        if (_native_threads_only)
01851               me->md.overlapped.overlapped.hEvent = me->md.thr_event;
01852 
01853     _PR_THREAD_LOCK(me);
01854        if (_PR_PENDING_INTERRUPT(me)) {
01855               me->flags &= ~_PR_INTERRUPT;
01856               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01857        _PR_THREAD_UNLOCK(me);
01858               return -1;
01859        }
01860     me->io_pending = PR_TRUE;
01861     me->state = _PR_IO_WAIT;
01862     _PR_THREAD_UNLOCK(me);
01863     me->io_fd = osfd;
01864 
01865     rv = WriteFile((HANDLE)osfd,
01866                    buf, 
01867                    amount,
01868                    &bytes,
01869                    &(me->md.overlapped.overlapped));
01870     if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
01871        _PR_THREAD_LOCK(me);
01872         me->io_pending = PR_FALSE;
01873         me->state = _PR_RUNNING;
01874               if (_PR_PENDING_INTERRUPT(me)) {
01875                      me->flags &= ~_PR_INTERRUPT;
01876                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01877                      _PR_THREAD_UNLOCK(me);
01878                      return -1;
01879               }
01880               _PR_THREAD_UNLOCK(me);
01881 
01882               _PR_MD_MAP_WRITE_ERROR(err);
01883         return -1;
01884     }
01885 
01886     if (_native_threads_only && rv) {
01887         _native_thread_io_nowait(me, rv, bytes);
01888     } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
01889         PR_ASSERT(0);
01890         return -1;
01891     }
01892 
01893     PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
01894 
01895     if (me->io_suspended) {
01896         if (_PR_PENDING_INTERRUPT(me)) {
01897             me->flags &= ~_PR_INTERRUPT;
01898             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
01899         } else {
01900             PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
01901         }
01902         return -1;
01903     }
01904 
01905     if (me->md.blocked_io_status == 0) {
01906               _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
01907         return -1;
01908     }
01909 
01910     PR_ASSERT(me->io_pending == PR_FALSE);
01911 
01912     return me->md.blocked_io_bytes;
01913 }
01914 
01915 PRInt32
01916 _PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
01917               const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
01918 {
01919     PRInt32 osfd = fd->secret->md.osfd;
01920     PRInt32 rv;
01921 
01922     if (!fd->secret->md.io_model_committed) {
01923         rv = _md_MakeNonblock((HANDLE)osfd);
01924         PR_ASSERT(0 != rv);
01925         fd->secret->md.io_model_committed = PR_TRUE;
01926     }
01927     if (_NT_USE_NB_IO(fd))
01928         return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
01929     else
01930         return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
01931 }
01932 
01933 PRInt32
01934 _PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
01935                 PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
01936 {
01937     PRInt32 osfd = fd->secret->md.osfd;
01938     PRInt32 rv;
01939 
01940     if (!fd->secret->md.io_model_committed) {
01941         rv = _md_MakeNonblock((HANDLE)osfd);
01942         PR_ASSERT(0 != rv);
01943         fd->secret->md.io_model_committed = PR_TRUE;
01944     }
01945     if (_NT_USE_NB_IO(fd))
01946         return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
01947     else
01948         return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
01949 }
01950 
01951 /* XXXMB - for now this is a sockets call only */
01952 PRInt32
01953 _PR_MD_WRITEV(PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
01954 {
01955     PRInt32 osfd = fd->secret->md.osfd;
01956     int index;
01957     int sent = 0;
01958     int rv;
01959 
01960     if (_NT_USE_NB_IO(fd)) {
01961         if (!fd->secret->md.io_model_committed) {
01962             rv = _md_MakeNonblock((HANDLE)osfd);
01963             PR_ASSERT(0 != rv);
01964             fd->secret->md.io_model_committed = PR_TRUE;
01965         }
01966         return _nt_nonblock_writev(fd, iov, iov_size, timeout);
01967     }
01968 
01969     for (index=0; index<iov_size; index++) {
01970         rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
01971                                           timeout);
01972         if (rv > 0) 
01973             sent += rv;
01974         if ( rv != iov[index].iov_len ) {
01975             if (sent <= 0)
01976                 return -1;
01977             return -1;
01978         }
01979     }
01980 
01981     return sent;
01982 }
01983 
01984 PRInt32
01985 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
01986 {
01987     PRInt32 rv;
01988 
01989     rv = listen(fd->secret->md.osfd, backlog);
01990        if (rv < 0)
01991               _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
01992        return(rv);
01993 }
01994 
01995 PRInt32
01996 _PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
01997 {
01998     PRInt32 rv;
01999 
02000     rv = shutdown(fd->secret->md.osfd, how);
02001        if (rv < 0)
02002               _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
02003        return(rv);
02004 }
02005 
02006 PRStatus
02007 _PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
02008 {
02009     PRInt32 rv;
02010 
02011     rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
02012     if (rv==0)
02013               return PR_SUCCESS;
02014        else {
02015               _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
02016               return PR_FAILURE;
02017        }
02018 }
02019 
02020 PRStatus
02021 _PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
02022 {
02023     PRInt32 rv;
02024 
02025     /*
02026      * NT has a bug that, when invoked on a socket accepted by
02027      * AcceptEx(), getpeername() returns an all-zero peer address.
02028      * To work around this bug, we store the peer's address (returned
02029      * by AcceptEx()) with the socket fd and use the cached peer
02030      * address if the socket is an accepted socket.
02031      */
02032 
02033     if (fd->secret->md.accepted_socket) {
02034         INT seconds;
02035         INT bytes = sizeof(seconds);
02036 
02037         /*
02038          * Determine if the socket is connected.
02039          */
02040 
02041         rv = getsockopt(fd->secret->md.osfd, 
02042                         SOL_SOCKET,
02043                         SO_CONNECT_TIME,
02044                         (char *) &seconds,
02045                         (PINT) &bytes);
02046         if (rv == NO_ERROR) {
02047             if (seconds == 0xffffffff) {
02048                 PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
02049                 return PR_FAILURE;
02050             }
02051             *len = PR_NETADDR_SIZE(&fd->secret->md.peer_addr);
02052             memcpy(addr, &fd->secret->md.peer_addr, *len);
02053             return PR_SUCCESS;
02054         } else {
02055             _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
02056             return PR_FAILURE;
02057         }
02058     } else { 
02059         rv = getpeername((SOCKET)fd->secret->md.osfd,
02060                          (struct sockaddr *) addr, len);
02061         if (rv == 0) {
02062             return PR_SUCCESS;
02063         } else {
02064             _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
02065             return PR_FAILURE;
02066         }
02067     }
02068 }
02069 
02070 PRStatus
02071 _PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
02072 {
02073     PRInt32 rv;
02074 
02075     rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
02076     if (rv==0)
02077               return PR_SUCCESS;
02078        else {
02079               _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
02080               return PR_FAILURE;
02081        }
02082 }
02083 
02084 PRStatus
02085 _PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
02086 {
02087     PRInt32 rv;
02088 
02089     rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
02090     if (rv==0)
02091               return PR_SUCCESS;
02092        else {
02093               _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
02094               return PR_FAILURE;
02095        }
02096 }
02097 
02098 /* --- FILE IO ----------------------------------------------------------- */
02099 
02100 PRInt32
02101 _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
02102 {
02103     HANDLE file;
02104     PRInt32 access = 0;
02105     PRInt32 flags = 0;
02106     PRInt32 flag6 = 0;
02107     
02108     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
02109  
02110     if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
02111     if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
02112 
02113     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
02114         flags = CREATE_NEW;
02115     else if (osflags & PR_CREATE_FILE)
02116         flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
02117     else if (osflags & PR_TRUNCATE) flags = TRUNCATE_EXISTING;
02118     else flags = OPEN_EXISTING;
02119 
02120 
02121     flag6 |= FILE_FLAG_OVERLAPPED;
02122 
02123     file = CreateFile(name, 
02124                       access, 
02125                       FILE_SHARE_READ|FILE_SHARE_WRITE,
02126                       NULL,
02127                       flags, 
02128                       flag6,
02129                       NULL);
02130     if (file == INVALID_HANDLE_VALUE) {
02131         _PR_MD_MAP_OPEN_ERROR(GetLastError());
02132         return -1;
02133     }
02134 
02135     if (osflags & PR_APPEND) {
02136         if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
02137             _PR_MD_MAP_LSEEK_ERROR(GetLastError());
02138             CloseHandle(file);
02139             return -1;
02140         }
02141     }
02142 
02143     return (PRInt32)file;
02144 }
02145 
02146 PRInt32
02147 _PR_MD_OPEN_FILE(const char *name, PRIntn osflags, PRIntn mode)
02148 {
02149     HANDLE file;
02150     PRInt32 access = 0;
02151     PRInt32 flags = 0;
02152     PRInt32 flag6 = 0;
02153     SECURITY_ATTRIBUTES sa;
02154     LPSECURITY_ATTRIBUTES lpSA = NULL;
02155     PSECURITY_DESCRIPTOR pSD = NULL;
02156     PACL pACL = NULL;
02157 
02158     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
02159  
02160     if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
02161     if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
02162 
02163     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
02164         flags = CREATE_NEW;
02165     else if (osflags & PR_CREATE_FILE)
02166         flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
02167     else if (osflags & PR_TRUNCATE) flags = TRUNCATE_EXISTING;
02168     else flags = OPEN_EXISTING;
02169 
02170 
02171     flag6 |= FILE_FLAG_OVERLAPPED;
02172 
02173     if (osflags & PR_CREATE_FILE) {
02174         if (_PR_NT_MakeSecurityDescriptorACL(mode, fileAccessTable,
02175                 &pSD, &pACL) == PR_SUCCESS) {
02176             sa.nLength = sizeof(sa);
02177             sa.lpSecurityDescriptor = pSD;
02178             sa.bInheritHandle = FALSE;
02179             lpSA = &sa;
02180         }
02181     }
02182     file = CreateFile(name, 
02183                       access, 
02184                       FILE_SHARE_READ|FILE_SHARE_WRITE,
02185                       lpSA,
02186                       flags, 
02187                       flag6,
02188                       NULL);
02189     if (lpSA != NULL) {
02190         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
02191     }
02192     if (file == INVALID_HANDLE_VALUE) {
02193         _PR_MD_MAP_OPEN_ERROR(GetLastError());
02194         return -1;
02195     }
02196 
02197     if (osflags & PR_APPEND) {
02198         if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
02199             _PR_MD_MAP_LSEEK_ERROR(GetLastError());
02200             CloseHandle(file);
02201             return -1;
02202         }
02203     }
02204 
02205     return (PRInt32)file;
02206 }
02207 
02208 PRInt32 
02209 _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
02210 {
02211     PRInt32 f = fd->secret->md.osfd;
02212     PRUint32 bytes;
02213     int rv, err;
02214     LONG hiOffset = 0;
02215     LONG loOffset;
02216 
02217     if (!fd->secret->md.sync_file_io) {
02218         PRThread *me = _PR_MD_CURRENT_THREAD();
02219 
02220         if (me->io_suspended) {
02221             PR_SetError(PR_INVALID_STATE_ERROR, 0);
02222             return -1;
02223         }
02224 
02225         memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
02226 
02227         me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
02228         PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
02229 
02230         if (fd->secret->inheritable == _PR_TRI_TRUE) {
02231             rv = ReadFile((HANDLE)f, 
02232                           (LPVOID)buf, 
02233                           len, 
02234                           &bytes, 
02235                           &me->md.overlapped.overlapped);
02236             if (rv != 0) {
02237                 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
02238                 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
02239                 return bytes;
02240             }
02241             err = GetLastError();
02242             if (err == ERROR_IO_PENDING) {
02243                 rv = GetOverlappedResult((HANDLE)f,
02244                         &me->md.overlapped.overlapped, &bytes, TRUE);
02245                 if (rv != 0) {
02246                     loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
02247                     PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
02248                     return bytes;
02249                 }
02250                 err = GetLastError();
02251             }
02252             if (err == ERROR_HANDLE_EOF) {
02253                 return 0;
02254             } else {
02255                 _PR_MD_MAP_READ_ERROR(err);
02256                 return -1;
02257             }
02258         } else {
02259             if (!fd->secret->md.io_model_committed) {
02260                 rv = _md_Associate((HANDLE)f);
02261                 PR_ASSERT(rv != 0);
02262                 fd->secret->md.io_model_committed = PR_TRUE;
02263             }
02264 
02265                      if (_native_threads_only)
02266                      me->md.overlapped.overlapped.hEvent = me->md.thr_event;
02267 
02268                      _PR_THREAD_LOCK(me);
02269                      if (_PR_PENDING_INTERRUPT(me)) {
02270                             me->flags &= ~_PR_INTERRUPT;
02271                             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02272                             _PR_THREAD_UNLOCK(me);
02273                             return -1;
02274                      }
02275                      me->io_pending = PR_TRUE;
02276                      me->state = _PR_IO_WAIT;
02277                      _PR_THREAD_UNLOCK(me);
02278                      me->io_fd = f;
02279 
02280             rv = ReadFile((HANDLE)f, 
02281                           (LPVOID)buf, 
02282                           len, 
02283                           &bytes, 
02284                           &me->md.overlapped.overlapped);
02285             if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
02286                             _PR_THREAD_LOCK(me);
02287                             me->io_pending = PR_FALSE;
02288                             me->state = _PR_RUNNING;
02289                             if (_PR_PENDING_INTERRUPT(me)) {
02290                                    me->flags &= ~_PR_INTERRUPT;
02291                                    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02292                                    _PR_THREAD_UNLOCK(me);
02293                                    return -1;
02294                             }
02295                             _PR_THREAD_UNLOCK(me);
02296 
02297                 if (err == ERROR_HANDLE_EOF) {
02298                     return 0;
02299                 }
02300                 _PR_MD_MAP_READ_ERROR(err);
02301                 return -1;
02302             }
02303 
02304             if (_native_threads_only && rv) {
02305                 _native_thread_io_nowait(me, rv, bytes);
02306             } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
02307                 PR_ASSERT(0);
02308                 return -1;
02309             }
02310 
02311             PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
02312 
02313             if (me->io_suspended) {
02314                 if (_PR_PENDING_INTERRUPT(me)) {
02315                     me->flags &= ~_PR_INTERRUPT;
02316                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02317                 } else {
02318                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
02319                 }
02320                 return -1;
02321             }
02322 
02323             if (me->md.blocked_io_status == 0) {
02324                 if (me->md.blocked_io_error == ERROR_HANDLE_EOF) {
02325                     return 0;
02326                 }
02327                 _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
02328                 return -1;
02329             }
02330 
02331             SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
02332     
02333             PR_ASSERT(me->io_pending == PR_FALSE);
02334 
02335             return me->md.blocked_io_bytes;
02336         }
02337     } else {
02338 
02339         rv = ReadFile((HANDLE)f,
02340                       (LPVOID)buf,
02341                       len,
02342                       &bytes,
02343                       NULL);
02344         if (rv == 0) {
02345             err = GetLastError();
02346             /* ERROR_HANDLE_EOF can only be returned by async io */
02347             PR_ASSERT(err != ERROR_HANDLE_EOF);
02348             if (err == ERROR_BROKEN_PIPE) {
02349                 /* The write end of the pipe has been closed. */ 
02350                 return 0;
02351             }
02352             _PR_MD_MAP_READ_ERROR(err);
02353             return -1;
02354         }
02355         return bytes;
02356     }
02357 }
02358 
02359 PRInt32
02360 _PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
02361 {
02362     PRInt32 f = fd->secret->md.osfd;
02363     PRInt32 bytes;
02364     int rv, err;
02365     LONG hiOffset = 0;
02366     LONG loOffset;
02367     LARGE_INTEGER offset; /* use for the calculation of the new offset */
02368 
02369     if (!fd->secret->md.sync_file_io) {
02370         PRThread *me = _PR_MD_CURRENT_THREAD();
02371 
02372         if (me->io_suspended) {
02373             PR_SetError(PR_INVALID_STATE_ERROR, 0);
02374             return -1;
02375         }
02376 
02377         memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
02378 
02379         me->md.overlapped.overlapped.Offset = SetFilePointer((HANDLE)f, 0, &me->md.overlapped.overlapped.OffsetHigh, FILE_CURRENT);
02380         PR_ASSERT((me->md.overlapped.overlapped.Offset != 0xffffffff) || (GetLastError() == NO_ERROR));
02381 
02382         if (fd->secret->inheritable == _PR_TRI_TRUE) {
02383             rv = WriteFile((HANDLE)f, 
02384                           (LPVOID)buf, 
02385                           len, 
02386                           &bytes, 
02387                           &me->md.overlapped.overlapped);
02388             if (rv != 0) {
02389                 loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
02390                 PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
02391                 return bytes;
02392             }
02393             err = GetLastError();
02394             if (err == ERROR_IO_PENDING) {
02395                 rv = GetOverlappedResult((HANDLE)f,
02396                         &me->md.overlapped.overlapped, &bytes, TRUE);
02397                 if (rv != 0) {
02398                     loOffset = SetFilePointer((HANDLE)f, bytes, &hiOffset, FILE_CURRENT);
02399                     PR_ASSERT((loOffset != 0xffffffff) || (GetLastError() == NO_ERROR));
02400                     return bytes;
02401                 }
02402                 err = GetLastError();
02403             }
02404             _PR_MD_MAP_READ_ERROR(err);
02405             return -1;
02406         } else {
02407             if (!fd->secret->md.io_model_committed) {
02408                 rv = _md_Associate((HANDLE)f);
02409                 PR_ASSERT(rv != 0);
02410                 fd->secret->md.io_model_committed = PR_TRUE;
02411             }
02412                      if (_native_threads_only)
02413                      me->md.overlapped.overlapped.hEvent = me->md.thr_event;
02414 
02415                      _PR_THREAD_LOCK(me);
02416                      if (_PR_PENDING_INTERRUPT(me)) {
02417                             me->flags &= ~_PR_INTERRUPT;
02418                             PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02419                             _PR_THREAD_UNLOCK(me);
02420                             return -1;
02421                      }
02422                      me->io_pending = PR_TRUE;
02423                      me->state = _PR_IO_WAIT;
02424                      _PR_THREAD_UNLOCK(me);
02425                      me->io_fd = f;
02426 
02427             rv = WriteFile((HANDLE)f, 
02428                            buf, 
02429                            len, 
02430                            &bytes, 
02431                            &(me->md.overlapped.overlapped));
02432             if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
02433                             _PR_THREAD_LOCK(me);
02434                             me->io_pending = PR_FALSE;
02435                             me->state = _PR_RUNNING;
02436                             if (_PR_PENDING_INTERRUPT(me)) {
02437                                    me->flags &= ~_PR_INTERRUPT;
02438                                    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02439                                    _PR_THREAD_UNLOCK(me);
02440                                    return -1;
02441                             }
02442                             _PR_THREAD_UNLOCK(me);
02443 
02444                 _PR_MD_MAP_WRITE_ERROR(err);
02445                 return -1;
02446             }
02447 
02448             if (_native_threads_only && rv) {
02449                 _native_thread_io_nowait(me, rv, bytes);
02450             } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
02451                 PR_ASSERT(0);
02452                 return -1;
02453             }
02454 
02455             PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
02456 
02457             if (me->io_suspended) {
02458                 if (_PR_PENDING_INTERRUPT(me)) {
02459                     me->flags &= ~_PR_INTERRUPT;
02460                     PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
02461                 } else {
02462                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
02463                 }
02464                 return -1;
02465             }
02466 
02467             if (me->md.blocked_io_status == 0) {
02468                 _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
02469                 return -1;
02470             }
02471 
02472             /*
02473              * Moving the file pointer by a relative offset (FILE_CURRENT)
02474              * does not work with a file on a network drive exported by a
02475              * Win2K system.  We still don't know why.  A workaround is to
02476              * move the file pointer by an absolute offset (FILE_BEGIN).
02477              * (Bugzilla bug 70765)
02478              */
02479             offset.LowPart = me->md.overlapped.overlapped.Offset;
02480             offset.HighPart = me->md.overlapped.overlapped.OffsetHigh;
02481             offset.QuadPart += me->md.blocked_io_bytes;
02482 
02483             SetFilePointer((HANDLE)f, offset.LowPart, &offset.HighPart, FILE_BEGIN);
02484     
02485             PR_ASSERT(me->io_pending == PR_FALSE);
02486 
02487             return me->md.blocked_io_bytes;
02488         }
02489     } else {
02490         rv = WriteFile((HANDLE)f,
02491                        buf,
02492                        len,
02493                        &bytes,
02494                        NULL);
02495         if (rv == 0) {
02496             _PR_MD_MAP_WRITE_ERROR(GetLastError());
02497             return -1;
02498         }
02499         return bytes;
02500     }
02501 }
02502 
02503 PRInt32
02504 _PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
02505 {
02506     PRInt32 result;
02507 
02508     if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
02509               PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
02510         return -1;
02511     }
02512     return result;
02513 }
02514 
02515 PRInt32
02516 _PR_MD_PIPEAVAILABLE(PRFileDesc *fd)
02517 {
02518     if (NULL == fd)
02519               PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
02520        else
02521               PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
02522     return -1;
02523 }
02524 
02525 PROffset32
02526 _PR_MD_LSEEK(PRFileDesc *fd, PROffset32 offset, PRSeekWhence whence)
02527 {
02528     DWORD moveMethod;
02529     PROffset32 rv;
02530 
02531     switch (whence) {
02532         case PR_SEEK_SET:
02533             moveMethod = FILE_BEGIN;
02534             break;
02535         case PR_SEEK_CUR:
02536             moveMethod = FILE_CURRENT;
02537             break;
02538         case PR_SEEK_END:
02539             moveMethod = FILE_END;
02540             break;
02541         default:
02542             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
02543             return -1;
02544     }
02545 
02546     rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, moveMethod);
02547 
02548     /*
02549      * If the lpDistanceToMoveHigh argument (third argument) is
02550      * NULL, SetFilePointer returns 0xffffffff on failure.
02551      */
02552     if (-1 == rv) {
02553         _PR_MD_MAP_LSEEK_ERROR(GetLastError());
02554     }
02555     return rv;
02556 }
02557 
02558 PROffset64
02559 _PR_MD_LSEEK64(PRFileDesc *fd, PROffset64 offset, PRSeekWhence whence)
02560 {
02561     DWORD moveMethod;
02562     LARGE_INTEGER li;
02563     DWORD err;
02564 
02565     switch (whence) {
02566         case PR_SEEK_SET:
02567             moveMethod = FILE_BEGIN;
02568             break;
02569         case PR_SEEK_CUR:
02570             moveMethod = FILE_CURRENT;
02571             break;
02572         case PR_SEEK_END:
02573             moveMethod = FILE_END;
02574             break;
02575         default:
02576             PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
02577             return -1;
02578     }
02579 
02580     li.QuadPart = offset;
02581     li.LowPart = SetFilePointer((HANDLE)fd->secret->md.osfd,
02582             li.LowPart, &li.HighPart, moveMethod);
02583 
02584     if (0xffffffff == li.LowPart && (err = GetLastError()) != NO_ERROR) {
02585         _PR_MD_MAP_LSEEK_ERROR(err);
02586         li.QuadPart = -1;
02587     }
02588     return li.QuadPart;
02589 }
02590 
02591 /*
02592  * This is documented to succeed on read-only files, but Win32's
02593  * FlushFileBuffers functions fails with "access denied" in such a
02594  * case.  So we only signal an error if the error is *not* "access
02595  * denied".
02596  */
02597 PRInt32
02598 _PR_MD_FSYNC(PRFileDesc *fd)
02599 {
02600     /*
02601      * From the documentation:
02602      *
02603      *    On Windows NT, the function FlushFileBuffers fails if hFile
02604      *    is a handle to console output. That is because console
02605      *    output is not buffered. The function returns FALSE, and
02606      *    GetLastError returns ERROR_INVALID_HANDLE.
02607      *
02608      * On the other hand, on Win95, it returns without error.  I cannot
02609      * assume that 0, 1, and 2 are console, because if someone closes
02610      * System.out and then opens a file, they might get file descriptor
02611      * 1.  An error on *that* version of 1 should be reported, whereas
02612      * an error on System.out (which was the original 1) should be
02613      * ignored.  So I use isatty() to ensure that such an error was
02614      * because of this, and if it was, I ignore the error.
02615      */
02616 
02617     BOOL ok = FlushFileBuffers((HANDLE)fd->secret->md.osfd);
02618 
02619     if (!ok) {
02620        DWORD err = GetLastError();
02621 
02622        if (err != ERROR_ACCESS_DENIED) {  /* from winerror.h */
02623                      _PR_MD_MAP_FSYNC_ERROR(err);
02624            return -1;
02625        }
02626     }
02627     return 0;
02628 }
02629 
02630 PRInt32
02631 _PR_MD_CLOSE(PRInt32 osfd, PRBool socket)
02632 {
02633     PRInt32 rv;
02634     PRThread *me = _PR_MD_CURRENT_THREAD();
02635 
02636     if (socket)  {
02637         rv = closesocket((SOCKET)osfd);
02638         if (rv < 0)
02639             _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
02640     } else {
02641         rv = CloseHandle((HANDLE)osfd)?0:-1;
02642         if (rv < 0)
02643             _PR_MD_MAP_CLOSE_ERROR(GetLastError());
02644     }
02645 
02646     if (rv == 0 && me->io_suspended) {
02647         if (me->io_fd == osfd) {
02648             PRBool fWait;
02649 
02650             _PR_THREAD_LOCK(me);
02651             me->state = _PR_IO_WAIT;
02652             /* The IO could have completed on another thread just after
02653              * calling closesocket while the io_suspended flag was true.  
02654              * So we now grab the lock to do a safe check on io_pending to
02655              * see if we need to wait or not.
02656              */
02657             fWait = me->io_pending;
02658             me->io_suspended = PR_FALSE;
02659             me->md.interrupt_disabled = PR_TRUE;
02660             _PR_THREAD_UNLOCK(me);
02661 
02662             if (fWait)
02663                 _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
02664             PR_ASSERT(me->io_suspended ==  PR_FALSE);
02665             PR_ASSERT(me->io_pending ==  PR_FALSE);
02666             /*
02667              * I/O operation is no longer pending; the thread can now
02668              * run on any cpu
02669              */
02670             _PR_THREAD_LOCK(me);
02671             me->md.interrupt_disabled = PR_FALSE;
02672             me->md.thr_bound_cpu = NULL;
02673             me->io_suspended = PR_FALSE;
02674             me->io_pending = PR_FALSE;
02675             me->state = _PR_RUNNING;
02676             _PR_THREAD_UNLOCK(me);
02677         }
02678     }
02679     return rv;
02680 }
02681 
02682 PRStatus
02683 _PR_MD_SET_FD_INHERITABLE(PRFileDesc *fd, PRBool inheritable)
02684 {
02685     BOOL rv;
02686 
02687     if (fd->secret->md.io_model_committed) {
02688         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
02689         return PR_FAILURE;
02690     }
02691     rv = SetHandleInformation(
02692             (HANDLE)fd->secret->md.osfd,
02693             HANDLE_FLAG_INHERIT,
02694             inheritable ? HANDLE_FLAG_INHERIT : 0);
02695     if (0 == rv) {
02696         _PR_MD_MAP_DEFAULT_ERROR(GetLastError());
02697         return PR_FAILURE;
02698     }
02699     return PR_SUCCESS;
02700 } 
02701 
02702 void
02703 _PR_MD_INIT_FD_INHERITABLE(PRFileDesc *fd, PRBool imported)
02704 {
02705     if (imported) {
02706         fd->secret->inheritable = _PR_TRI_UNKNOWN;
02707     } else {
02708         fd->secret->inheritable = _PR_TRI_FALSE;
02709     }
02710 }
02711 
02712 void
02713 _PR_MD_QUERY_FD_INHERITABLE(PRFileDesc *fd)
02714 {
02715     DWORD flags;
02716 
02717     PR_ASSERT(_PR_TRI_UNKNOWN == fd->secret->inheritable);
02718     if (fd->secret->md.io_model_committed) {
02719         return;
02720     }
02721     if (GetHandleInformation((HANDLE)fd->secret->md.osfd, &flags)) {
02722         if (flags & HANDLE_FLAG_INHERIT) {
02723             fd->secret->inheritable = _PR_TRI_TRUE;
02724         } else {
02725             fd->secret->inheritable = _PR_TRI_FALSE;
02726         }
02727     }
02728 }
02729 
02730 
02731 /* --- DIR IO ------------------------------------------------------------ */
02732 #define GetFileFromDIR(d)       (d)->d_entry.cFileName
02733 #define FileIsHidden(d)       ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
02734 
02735 void FlipSlashes(char *cp, int len)
02736 {
02737     while (--len >= 0) {
02738         if (cp[0] == '/') {
02739             cp[0] = PR_DIRECTORY_SEPARATOR;
02740         }
02741         cp = _mbsinc(cp);
02742     }
02743 } /* end FlipSlashes() */
02744 
02745 /*
02746 **
02747 ** Local implementations of standard Unix RTL functions which are not provided
02748 ** by the VC RTL.
02749 **
02750 */
02751 
02752 PRStatus
02753 _PR_MD_CLOSE_DIR(_MDDir *d)
02754 {
02755     if ( d ) {
02756         if (FindClose( d->d_hdl )) {
02757             d->magic = (PRUint32)-1;
02758             return PR_SUCCESS;
02759         } else {
02760             _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
02761             return PR_FAILURE;
02762         }
02763     }
02764     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
02765     return PR_FAILURE;
02766 }
02767 
02768 
02769 PRStatus
02770 _PR_MD_OPEN_DIR(_MDDir *d, const char *name)
02771 {
02772     char filename[ MAX_PATH ];
02773     int len;
02774 
02775     len = strlen(name);
02776     /* Need 5 bytes for \*.* and the trailing null byte. */
02777     if (len + 5 > MAX_PATH) {
02778         PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
02779         return PR_FAILURE;
02780     }
02781     strcpy(filename, name);
02782 
02783     /*
02784      * If 'name' ends in a slash or backslash, do not append
02785      * another backslash.
02786      */
02787     if (IsPrevCharSlash(filename, filename + len)) {
02788         len--;
02789     }
02790     strcpy(&filename[len], "\\*.*");
02791     FlipSlashes( filename, strlen(filename) );
02792 
02793     d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
02794     if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
02795               _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
02796         return PR_FAILURE;
02797     }
02798     d->firstEntry = PR_TRUE;
02799     d->magic = _MD_MAGIC_DIR;
02800     return PR_SUCCESS;
02801 }
02802 
02803 char *
02804 _PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
02805 {
02806     PRInt32 err;
02807     BOOL rv;
02808     char *fileName;
02809 
02810     if ( d ) {
02811         while (1) {
02812             if (d->firstEntry) {
02813                 d->firstEntry = PR_FALSE;
02814                 rv = 1;
02815             } else {
02816                 rv = FindNextFile(d->d_hdl, &(d->d_entry));
02817             }
02818             if (rv == 0) {
02819                 break;
02820             }
02821             fileName = GetFileFromDIR(d);
02822             if ( (flags & PR_SKIP_DOT) &&
02823                  (fileName[0] == '.') && (fileName[1] == '\0'))
02824                  continue;
02825             if ( (flags & PR_SKIP_DOT_DOT) &&
02826                  (fileName[0] == '.') && (fileName[1] == '.') &&
02827                  (fileName[2] == '\0'))
02828                  continue;
02829             if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
02830                  continue;
02831             return fileName;
02832         }
02833        err = GetLastError();
02834        PR_ASSERT(NO_ERROR != err);
02835                      _PR_MD_MAP_READDIR_ERROR(err);
02836        return NULL;
02837               }
02838     PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
02839     return NULL;
02840 }
02841 
02842 PRInt32
02843 _PR_MD_DELETE(const char *name)
02844 {
02845     if (DeleteFile(name)) {
02846               return 0;
02847        } else {
02848               _PR_MD_MAP_DELETE_ERROR(GetLastError());
02849               return -1;
02850        }
02851 }
02852 
02853 void
02854 _PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
02855 {
02856     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
02857     CopyMemory(prtm, filetime, sizeof(PRTime));
02858 #ifdef __GNUC__
02859     *prtm = (*prtm - _pr_filetime_offset) / 10LL;
02860 #else
02861     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
02862 #endif
02863 
02864 #ifdef DEBUG
02865     /* Doublecheck our calculation. */
02866     {
02867         SYSTEMTIME systime;
02868         PRExplodedTime etm;
02869         PRTime cmp; /* for comparison */
02870         BOOL rv;
02871 
02872         rv = FileTimeToSystemTime(filetime, &systime);
02873         PR_ASSERT(0 != rv);
02874 
02875         /*
02876          * PR_ImplodeTime ignores wday and yday.
02877          */
02878         etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
02879         etm.tm_sec = systime.wSecond;
02880         etm.tm_min = systime.wMinute;
02881         etm.tm_hour = systime.wHour;
02882         etm.tm_mday = systime.wDay;
02883         etm.tm_month = systime.wMonth - 1;
02884         etm.tm_year = systime.wYear;
02885         /*
02886          * It is not well-documented what time zone the FILETIME's
02887          * are in.  WIN32_FIND_DATA is documented to be in UTC (GMT).
02888          * But BY_HANDLE_FILE_INFORMATION is unclear about this.
02889          * By our best judgement, we assume that FILETIME is in UTC.
02890          */
02891         etm.tm_params.tp_gmt_offset = 0;
02892         etm.tm_params.tp_dst_offset = 0;
02893         cmp = PR_ImplodeTime(&etm);
02894 
02895         /*
02896          * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
02897          * microseconds to milliseconds before doing the comparison.
02898          */
02899         PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
02900     }
02901 #endif /* DEBUG */
02902 }
02903 
02904 PRInt32
02905 _PR_MD_STAT(const char *fn, struct stat *info)
02906 {
02907     PRInt32 rv;
02908 
02909     rv = _stat(fn, (struct _stat *)info);
02910     if (-1 == rv) {
02911         /*
02912          * Check for MSVC runtime library _stat() bug.
02913          * (It's really a bug in FindFirstFile().)
02914          * If a pathname ends in a backslash or slash,
02915          * e.g., c:\temp\ or c:/temp/, _stat() will fail.
02916          * Note: a pathname ending in a slash (e.g., c:/temp/)
02917          * can be handled by _stat() on NT but not on Win95.
02918          *
02919          * We remove the backslash or slash at the end and
02920          * try again.
02921          */
02922 
02923         int len = strlen(fn);
02924         if (len > 0 && len <= _MAX_PATH
02925                 && IsPrevCharSlash(fn, fn + len)) {
02926             char newfn[_MAX_PATH + 1];
02927 
02928             strcpy(newfn, fn);
02929             newfn[len - 1] = '\0';
02930             rv = _stat(newfn, (struct _stat *)info);
02931         }
02932     }
02933 
02934     if (-1 == rv) {
02935         _PR_MD_MAP_STAT_ERROR(errno);
02936     }
02937     return rv;
02938 }
02939 
02940 #define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
02941 
02942 static PRBool
02943 IsPrevCharSlash(const char *str, const char *current)
02944 {
02945     const char *prev;
02946 
02947     if (str >= current)
02948         return PR_FALSE;
02949     prev = _mbsdec(str, current);
02950     return (prev == current - 1) && _PR_IS_SLASH(*prev);
02951 }
02952 
02953 /*
02954  * IsRootDirectory --
02955  *
02956  * Return PR_TRUE if the pathname 'fn' is a valid root directory,
02957  * else return PR_FALSE.  The char buffer pointed to by 'fn' must
02958  * be writable.  During the execution of this function, the contents
02959  * of the buffer pointed to by 'fn' may be modified, but on return
02960  * the original contents will be restored.  'buflen' is the size of
02961  * the buffer pointed to by 'fn'.
02962  *
02963  * Root directories come in three formats:
02964  * 1. / or \, meaning the root directory of the current drive.
02965  * 2. C:/ or C:\, where C is a drive letter.
02966  * 3. \<server name><share point name>\ or
02967  *    \<server name><share point name>, meaning the root directory
02968  *    of a UNC (Universal Naming Convention) name.
02969  */
02970 
02971 static PRBool
02972 IsRootDirectory(char *fn, size_t buflen)
02973 {
02974     char *p;
02975     PRBool slashAdded = PR_FALSE;
02976     PRBool rv = PR_FALSE;
02977 
02978     if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
02979         return PR_TRUE;
02980     }
02981 
02982     if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
02983             && fn[3] == '\0') {
02984         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
02985         return rv;
02986     }
02987 
02988     /* The UNC root directory */
02989 
02990     if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
02991         /* The 'server' part should have at least one character. */
02992         p = &fn[2];
02993         if (*p == '\0' || _PR_IS_SLASH(*p)) {
02994             return PR_FALSE;
02995         }
02996 
02997         /* look for the next slash */
02998         do {
02999             p = _mbsinc(p);
03000         } while (*p != '\0' && !_PR_IS_SLASH(*p));
03001         if (*p == '\0') {
03002             return PR_FALSE;
03003         }
03004 
03005         /* The 'share' part should have at least one character. */
03006         p++;
03007         if (*p == '\0' || _PR_IS_SLASH(*p)) {
03008             return PR_FALSE;
03009         }
03010 
03011         /* look for the final slash */
03012         do {
03013             p = _mbsinc(p);
03014         } while (*p != '\0' && !_PR_IS_SLASH(*p));
03015         if (_PR_IS_SLASH(*p) && p[1] != '\0') {
03016             return PR_FALSE;
03017         }
03018         if (*p == '\0') {
03019             /*
03020              * GetDriveType() doesn't work correctly if the
03021              * path is of the form \\server\share, so we add
03022              * a final slash temporarily.
03023              */
03024             if ((p + 1) < (fn + buflen)) {
03025                 *p++ = '\\';
03026                 *p = '\0';
03027                 slashAdded = PR_TRUE;
03028             } else {
03029                 return PR_FALSE; /* name too long */
03030             }
03031         }
03032         rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
03033         /* restore the 'fn' buffer */
03034         if (slashAdded) {
03035             *--p = '\0';
03036         }
03037     }
03038     return rv;
03039 }
03040 
03041 PRInt32
03042 _PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
03043 {
03044     HANDLE hFindFile;
03045     WIN32_FIND_DATA findFileData;
03046     char pathbuf[MAX_PATH + 1];
03047     
03048     if (NULL == fn || '\0' == *fn) {
03049         PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
03050         return -1;
03051     }
03052 
03053     /*
03054      * FindFirstFile() expands wildcard characters.  So
03055      * we make sure the pathname contains no wildcard.
03056      */
03057     if (NULL != _mbspbrk(fn, "?*")) {
03058         PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
03059         return -1;
03060     }
03061 
03062     hFindFile = FindFirstFile(fn, &findFileData);
03063     if (INVALID_HANDLE_VALUE == hFindFile) {
03064         DWORD len;
03065         char *filePart;
03066 
03067         /*
03068          * FindFirstFile() does not work correctly on root directories.
03069          * It also doesn't work correctly on a pathname that ends in a
03070          * slash.  So we first check to see if the pathname specifies a
03071          * root directory.  If not, and if the pathname ends in a slash,
03072          * we remove the final slash and try again.
03073          */
03074 
03075         /*
03076          * If the pathname does not contain ., \, and /, it cannot be
03077          * a root directory or a pathname that ends in a slash.
03078          */
03079         if (NULL == _mbspbrk(fn, ".\\/")) {
03080             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
03081             return -1;
03082         } 
03083         len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
03084                 &filePart);
03085         if (0 == len) {
03086             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
03087             return -1;
03088         }
03089         if (len > sizeof(pathbuf)) {
03090             PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
03091             return -1;
03092         }
03093         if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
03094             info->type = PR_FILE_DIRECTORY;
03095             info->size = 0;
03096             /*
03097              * These timestamps don't make sense for root directories.
03098              */
03099             info->modifyTime = 0;
03100             info->creationTime = 0;
03101             return 0;
03102         }
03103         if (!IsPrevCharSlash(pathbuf, pathbuf + len)) {
03104             _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
03105             return -1;
03106         } else {
03107             pathbuf[len - 1] = '\0';
03108             hFindFile = FindFirstFile(pathbuf, &findFileData);
03109             if (INVALID_HANDLE_VALUE == hFindFile) {
03110                 _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
03111                 return -1;
03112             }
03113         }
03114     }
03115 
03116     FindClose(hFindFile);
03117 
03118     if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
03119         info->type = PR_FILE_DIRECTORY;
03120     } else {
03121         info->type = PR_FILE_FILE;
03122     }
03123 
03124     info->size = findFileData.nFileSizeHigh;
03125     info->size = (info->size << 32) + findFileData.nFileSizeLow;
03126 
03127     _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
03128 
03129     if (0 == findFileData.ftCreationTime.dwLowDateTime &&
03130             0 == findFileData.ftCreationTime.dwHighDateTime) {
03131         info->creationTime = info->modifyTime;
03132     } else {
03133         _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
03134                 &info->creationTime);
03135     }
03136 
03137     return 0;
03138 }
03139 
03140 PRInt32
03141 _PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
03142 {
03143     PRFileInfo64 info64;
03144     PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
03145     if (0 == rv)
03146     {
03147         info->type = info64.type;
03148         info->size = (PRUint32) info64.size;
03149         info->modifyTime = info64.modifyTime;
03150         info->creationTime = info64.creationTime;
03151     }
03152     return rv;
03153 }
03154 
03155 PRInt32
03156 _PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
03157 {
03158     int rv;
03159 
03160     BY_HANDLE_FILE_INFORMATION hinfo;
03161 
03162     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
03163     if (rv == FALSE) {
03164               _PR_MD_MAP_FSTAT_ERROR(GetLastError());
03165         return -1;
03166        }
03167 
03168     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
03169         info->type = PR_FILE_DIRECTORY;
03170     else
03171         info->type = PR_FILE_FILE;
03172 
03173     info->size = hinfo.nFileSizeHigh;
03174     info->size = (info->size << 32) + hinfo.nFileSizeLow;
03175 
03176     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
03177     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
03178 
03179     return 0;
03180 }
03181 
03182 PRInt32
03183 _PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
03184 {
03185     int rv;
03186 
03187     BY_HANDLE_FILE_INFORMATION hinfo;
03188 
03189     rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
03190     if (rv == FALSE) {
03191               _PR_MD_MAP_FSTAT_ERROR(GetLastError());
03192         return -1;
03193        }
03194 
03195     if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
03196         info->type = PR_FILE_DIRECTORY;
03197     else
03198         info->type = PR_FILE_FILE;
03199 
03200     info->size = hinfo.nFileSizeLow;
03201 
03202     _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
03203     _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
03204 
03205     return 0;
03206 }
03207 
03208 PRInt32
03209 _PR_MD_RENAME(const char *from, const char *to)
03210 {
03211     /* Does this work with dot-relative pathnames? */
03212     if (MoveFile(from, to)) {
03213               return 0;
03214        } else {
03215               _PR_MD_MAP_RENAME_ERROR(GetLastError());
03216               return -1;
03217        }
03218 }
03219 
03220 PRInt32
03221 _PR_MD_ACCESS(const char *name, PRAccessHow how)
03222 {
03223     PRInt32 rv;
03224 
03225     switch (how) {
03226       case PR_ACCESS_WRITE_OK:
03227         rv = _access(name, 02);
03228               break;
03229       case PR_ACCESS_READ_OK:
03230         rv = _access(name, 04);
03231               break;
03232       case PR_ACCESS_EXISTS:
03233         rv = _access(name, 00);
03234               break;
03235       default:
03236               PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
03237               return -1;
03238     }
03239        if (rv < 0) {
03240               _PR_MD_MAP_ACCESS_ERROR(errno);
03241     }
03242     return rv;
03243 }
03244 
03245 PRInt32
03246 _PR_MD_MKDIR(const char *name, PRIntn mode)
03247 {
03248     /* XXXMB - how to translate the "mode"??? */
03249     if (CreateDirectory(name, NULL)) {
03250         return 0;
03251     } else {
03252         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
03253         return -1;
03254     }
03255 }
03256 
03257 PRInt32
03258 _PR_MD_MAKE_DIR(const char *name, PRIntn mode)
03259 {
03260     BOOL rv;
03261     SECURITY_ATTRIBUTES sa;
03262     LPSECURITY_ATTRIBUTES lpSA = NULL;
03263     PSECURITY_DESCRIPTOR pSD = NULL;
03264     PACL pACL = NULL;
03265 
03266     if (_PR_NT_MakeSecurityDescriptorACL(mode, dirAccessTable,
03267             &pSD, &pACL) == PR_SUCCESS) {
03268         sa.nLength = sizeof(sa);
03269         sa.lpSecurityDescriptor = pSD;
03270         sa.bInheritHandle = FALSE;
03271         lpSA = &sa;
03272     }
03273     rv = CreateDirectory(name, lpSA);
03274     if (lpSA != NULL) {
03275         _PR_NT_FreeSecurityDescriptorACL(pSD, pACL);
03276     }
03277     if (rv) {
03278         return 0;
03279     } else {
03280         _PR_MD_MAP_MKDIR_ERROR(GetLastError());
03281         return -1;
03282     }
03283 }
03284 
03285 PRInt32
03286 _PR_MD_RMDIR(const char *name)
03287 {
03288     if (RemoveDirectory(name)) {
03289         return 0;
03290     } else {
03291         _PR_MD_MAP_RMDIR_ERROR(GetLastError());
03292         return -1;
03293     }
03294 }
03295 
03296 PRStatus
03297 _PR_MD_LOCKFILE(PRInt32 f)
03298 {
03299     PRInt32 rv, err;
03300     PRThread *me = _PR_MD_CURRENT_THREAD();
03301 
03302     if (me->io_suspended) {
03303         PR_SetError(PR_INVALID_STATE_ERROR, 0);
03304         return PR_FAILURE;
03305     }
03306 
03307     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
03308 
03309     _PR_THREAD_LOCK(me);
03310        if (_PR_PENDING_INTERRUPT(me)) {
03311               me->flags &= ~_PR_INTERRUPT;
03312               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03313        _PR_THREAD_UNLOCK(me);
03314               return -1;
03315        }
03316     me->io_pending = PR_TRUE;
03317     me->state = _PR_IO_WAIT;
03318     _PR_THREAD_UNLOCK(me);
03319 
03320     rv = LockFileEx((HANDLE)f, 
03321                     LOCKFILE_EXCLUSIVE_LOCK,
03322                     0,
03323                     0x7fffffff,
03324                     0,
03325                     &me->md.overlapped.overlapped);
03326 
03327     if (_native_threads_only) {
03328               _PR_THREAD_LOCK(me);
03329               me->io_pending = PR_FALSE;
03330               me->state = _PR_RUNNING;
03331               if (_PR_PENDING_INTERRUPT(me)) {
03332                      me->flags &= ~_PR_INTERRUPT;
03333                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03334                      _PR_THREAD_UNLOCK(me);
03335                      return PR_FAILURE;
03336               }
03337               _PR_THREAD_UNLOCK(me);
03338 
03339         if (rv == FALSE) {
03340             err = GetLastError();
03341             PR_ASSERT(err != ERROR_IO_PENDING);
03342             _PR_MD_MAP_LOCKF_ERROR(err);
03343             return PR_FAILURE;
03344         }
03345         return PR_SUCCESS;
03346     }
03347 
03348     /* HACK AROUND NT BUG
03349      * NT 3.51 has a bug.  In NT 3.51, if LockFileEx returns true, you
03350      * don't get any completion on the completion port.  This is a bug.
03351      *
03352      * They fixed it on NT4.0 so that you do get a completion.
03353      *
03354      * If we pretend we won't get a completion, NSPR gets confused later
03355      * when the unexpected completion arrives.  If we assume we do get
03356      * a completion, we hang on 3.51.  Worse, Microsoft informs me that the 
03357      * behavior varies on 3.51 depending on if you are using a network
03358      * file system or a local disk!
03359      *
03360      * Solution:  For now, _nt_version_gets_lockfile_completion is set
03361      * depending on whether or not this system is EITHER
03362      *      - running NT 4.0
03363      *      - running NT 3.51 with a service pack greater than 5.
03364      * 
03365      * In the meantime, this code may not work on network file systems.
03366      *
03367      */
03368 
03369     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
03370               _PR_THREAD_LOCK(me);
03371               me->io_pending = PR_FALSE;
03372               me->state = _PR_RUNNING;
03373               if (_PR_PENDING_INTERRUPT(me)) {
03374                      me->flags &= ~_PR_INTERRUPT;
03375                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03376                      _PR_THREAD_UNLOCK(me);
03377                      return PR_FAILURE;
03378               }
03379               _PR_THREAD_UNLOCK(me);
03380 
03381               _PR_MD_MAP_LOCKF_ERROR(err);
03382         return PR_FAILURE;
03383     }
03384 #ifdef _NEED_351_FILE_LOCKING_HACK
03385     else if (rv)  {
03386         /* If this is NT 3.51 and the file is local, then we won't get a 
03387          * completion back from LockFile when it succeeded.
03388          */
03389         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
03390             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
03391                 me->io_pending = PR_FALSE;
03392                 me->state = _PR_RUNNING;
03393                 return PR_SUCCESS; 
03394             }
03395         }
03396     }
03397 #endif /* _NEED_351_FILE_LOCKING_HACK */
03398 
03399     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
03400               _PR_THREAD_LOCK(me);
03401         me->io_pending = PR_FALSE;
03402         me->state = _PR_RUNNING;
03403               _PR_THREAD_UNLOCK(me);
03404         return PR_FAILURE;
03405     }
03406 
03407     if (me->md.blocked_io_status == 0) {
03408               _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
03409         return PR_FAILURE;
03410     }
03411 
03412     return PR_SUCCESS;
03413 }
03414 
03415 PRStatus
03416 _PR_MD_TLOCKFILE(PRInt32 f)
03417 {
03418     PRInt32 rv, err;
03419     PRThread *me = _PR_MD_CURRENT_THREAD();
03420 
03421     if (me->io_suspended) {
03422         PR_SetError(PR_INVALID_STATE_ERROR, 0);
03423         return PR_FAILURE;
03424     }
03425 
03426     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
03427 
03428     _PR_THREAD_LOCK(me);
03429        if (_PR_PENDING_INTERRUPT(me)) {
03430               me->flags &= ~_PR_INTERRUPT;
03431               PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03432        _PR_THREAD_UNLOCK(me);
03433               return -1;
03434        }
03435     me->io_pending = PR_TRUE;
03436     me->state = _PR_IO_WAIT;
03437     _PR_THREAD_UNLOCK(me);
03438 
03439     rv = LockFileEx((HANDLE)f, 
03440                     LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
03441                     0,
03442                     0x7fffffff,
03443                     0,
03444                     &me->md.overlapped.overlapped);
03445     if (_native_threads_only) {
03446               _PR_THREAD_LOCK(me);
03447               me->io_pending = PR_FALSE;
03448               me->state = _PR_RUNNING;
03449               if (_PR_PENDING_INTERRUPT(me)) {
03450                      me->flags &= ~_PR_INTERRUPT;
03451                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03452                      _PR_THREAD_UNLOCK(me);
03453                      return PR_FAILURE;
03454               }
03455               _PR_THREAD_UNLOCK(me);
03456 
03457         if (rv == FALSE) {
03458             err = GetLastError();
03459             PR_ASSERT(err != ERROR_IO_PENDING);
03460             _PR_MD_MAP_LOCKF_ERROR(err);
03461             return PR_FAILURE;
03462         }
03463         return PR_SUCCESS;
03464     }
03465     if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
03466               _PR_THREAD_LOCK(me);
03467               me->io_pending = PR_FALSE;
03468               me->state = _PR_RUNNING;
03469               if (_PR_PENDING_INTERRUPT(me)) {
03470                      me->flags &= ~_PR_INTERRUPT;
03471                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03472                      _PR_THREAD_UNLOCK(me);
03473                      return PR_FAILURE;
03474               }
03475               _PR_THREAD_UNLOCK(me);
03476 
03477         _PR_MD_MAP_LOCKF_ERROR(err);
03478         return PR_FAILURE;
03479     }
03480 #ifdef _NEED_351_FILE_LOCKING_HACK
03481     else if (rv)  {
03482         /* If this is NT 3.51 and the file is local, then we won't get a 
03483          * completion back from LockFile when it succeeded.
03484          */
03485         if (_nt_version_gets_lockfile_completion == PR_FALSE) {
03486             if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
03487                             _PR_THREAD_LOCK(me);
03488                             me->io_pending = PR_FALSE;
03489                             me->state = _PR_RUNNING;
03490                             if (_PR_PENDING_INTERRUPT(me)) {
03491                                    me->flags &= ~_PR_INTERRUPT;
03492                                    PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03493                                    _PR_THREAD_UNLOCK(me);
03494                                    return PR_FAILURE;
03495                             }
03496                             _PR_THREAD_UNLOCK(me);
03497 
03498                 return PR_SUCCESS; 
03499             }
03500         }
03501     }
03502 #endif /* _NEED_351_FILE_LOCKING_HACK */
03503 
03504     if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
03505               _PR_THREAD_LOCK(me);
03506               me->io_pending = PR_FALSE;
03507               me->state = _PR_RUNNING;
03508               if (_PR_PENDING_INTERRUPT(me)) {
03509                      me->flags &= ~_PR_INTERRUPT;
03510                      PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
03511                      _PR_THREAD_UNLOCK(me);
03512                      return PR_FAILURE;
03513               }
03514               _PR_THREAD_UNLOCK(me);
03515 
03516         return PR_FAILURE;
03517     }
03518 
03519     if (me->md.blocked_io_status == 0) {
03520               _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
03521         return PR_FAILURE;
03522     }
03523 
03524     return PR_SUCCESS;
03525 }
03526 
03527 
03528 PRStatus
03529 _PR_MD_UNLOCKFILE(PRInt32 f)
03530 {
03531     PRInt32 rv;
03532     PRThread *me = _PR_MD_CURRENT_THREAD();
03533 
03534     if (me->io_suspended) {
03535         PR_SetError(PR_INVALID_STATE_ERROR, 0);
03536         return PR_FAILURE;
03537     }
03538 
03539     memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED));
03540 
03541     rv = UnlockFileEx((HANDLE)f,
03542                       0,
03543                       0x7fffffff,
03544                       0,
03545                       &me->md.overlapped.overlapped);
03546 
03547     if (rv)
03548         return PR_SUCCESS;
03549     else {
03550         int err = GetLastError();
03551               _PR_MD_MAP_LOCKF_ERROR(err);
03552         return PR_FAILURE;
03553     }
03554 }
03555 
03556 void
03557 _PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
03558 {
03559     /*
03560      * On NT, we either call _md_Associate() or _md_MakeNonblock(),
03561      * depending on whether the socket is blocking or not.
03562      *
03563      * Once we associate a socket with the io completion port,
03564      * there is no way to disassociate it from the io completion
03565      * port.  So we have to call _md_Associate/_md_MakeNonblock
03566      * lazily.
03567      */
03568 }
03569 
03570 #ifdef _NEED_351_FILE_LOCKING_HACK
03571 /***************
03572 ** 
03573 ** Lockfile hacks
03574 **
03575 ** The following code is a hack to work around a microsoft bug with lockfile.
03576 ** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
03577 ** get a completion back for files that are on local disks.  So, we need to
03578 ** know if a file is local or remote so we can tell if we should expect 
03579 ** a completion.
03580 **
03581 ** The only way to check if a file is local or remote based on the handle is
03582 ** to get the serial number for the volume it is mounted on and then to 
03583 ** compare that with mounted drives.  This code caches the volume numbers of
03584 ** fixed disks and does a relatively quick check.
03585 **
03586 ** Locking:  Since the only thing we ever do when multithreaded is a 32bit
03587 **           assignment, we probably don't need locking.  It is included just
03588 **           case anyway.
03589 **
03590 ** Limitations:  Does not work on floppies because they are too slow
03591 **               Unknown if it will work on wierdo 3rd party file systems
03592 **
03593 ****************
03594 */
03595 
03596 /* There can only be 26 drive letters on NT */
03597 #define _PR_MAX_DRIVES 26
03598 
03599 _MDLock cachedVolumeLock;
03600 DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
03601 DWORD dwLastCachedDrive = 0;
03602 DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
03603 
03604 PRBool IsFileLocalInit()
03605 {
03606    TCHAR lpBuffer[_PR_MAX_DRIVES*5];
03607    DWORD nBufferLength = _PR_MAX_DRIVES*5;
03608    DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
03609    DWORD dwIndex = 0;
03610    DWORD dwDriveType;
03611    DWORD dwVolumeSerialNumber;
03612    DWORD dwDriveIndex = 0;
03613    DWORD oldmode = (DWORD) -1;
03614 
03615    _MD_NEW_LOCK(&cachedVolumeLock);
03616 
03617    nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
03618    if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength)
03619       return PR_FALSE;
03620 
03621    // Calling GetVolumeInformation on a removeable drive where the
03622    // disk is currently removed will cause a dialog box to the
03623    // console.  This is not good.
03624    // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
03625    // damn dialog.
03626 
03627    dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
03628    oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
03629 
03630    // now loop through the logical drives
03631    while(lpBuffer[dwIndex] != TEXT('\0'))
03632    {
03633       // skip the floppy drives.  This is *SLOW*
03634       if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
03635          /* Skip over floppies */;
03636       else
03637       {
03638          dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
03639 
03640          dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
03641 
03642          switch(dwDriveType)
03643          {
03644                // Ignore these drive types
03645             case 0:
03646             case 1:
03647             case DRIVE_REMOTE:
03648             default: // If the drive type is unknown, ignore it.
03649                break;
03650 
03651                // Removable media drives can have different serial numbers
03652                // at different times, so cache the current serial number
03653                // but keep track of them so they can be rechecked if necessary.
03654             case DRIVE_REMOVABLE:
03655 
03656                // CDROM is a removable media
03657             case DRIVE_CDROM: 
03658 
03659                // no idea if ramdisks can change serial numbers or not
03660                // but it doesn't hurt to treat them as removable.
03661               
03662             case DRIVE_RAMDISK: 
03663 
03664 
03665                // Here is where we keep track of removable drives.
03666                dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
03667 
03668                // removable drives fall through to fixed drives and get cached.
03669 
03670             case DRIVE_FIXED:
03671 
03672                // cache volume serial numbers. 
03673                if (GetVolumeInformation(
03674                    &lpBuffer[dwIndex],
03675                    NULL, 0,
03676                    &dwVolumeSerialNumber,
03677                    NULL, NULL, NULL, 0)
03678                   )
03679                   {
03680                      if (dwLastCachedDrive < dwDriveIndex)
03681                         dwLastCachedDrive = dwDriveIndex;
03682                      dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
03683                   }
03684  
03685                break;
03686          }
03687       }
03688 
03689       dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
03690    }
03691 
03692    if (oldmode != (DWORD) -1) {
03693        SetErrorMode(oldmode);
03694        oldmode = (DWORD) -1;
03695    }
03696 
03697    return PR_TRUE;
03698 }
03699 
03700 PRInt32 IsFileLocal(HANDLE hFile)
03701 {
03702    DWORD dwIndex = 0, dwMask;
03703    BY_HANDLE_FILE_INFORMATION Info;
03704    TCHAR szDrive[4] = TEXT("C:\\");
03705    DWORD dwVolumeSerialNumber;
03706    DWORD oldmode = (DWORD) -1;
03707    int rv = _PR_REMOTE_FILE;
03708 
03709    if (!GetFileInformationByHandle(hFile, &Info))
03710       return -1;
03711 
03712    // look to see if the volume serial number has been cached.
03713    _MD_LOCK(&cachedVolumeLock);
03714    while(dwIndex <= dwLastCachedDrive)
03715       if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
03716          return _PR_LOCAL_FILE;
03717    _MD_UNLOCK(&cachedVolumeLock);
03718 
03719    // volume serial number not found in the cache.  Check removable files.
03720    // removable drives are noted as a bitmask.  If the bit associated with 
03721    // a specific drive is set, then we should query its volume serial number
03722    // as its possible it has changed.
03723    dwMask = dwRemoveableDrivesToCheck;
03724    dwIndex = 0;
03725 
03726    while(dwMask)
03727    {
03728       while(!(dwMask & 1))
03729       {
03730          dwIndex++;
03731          dwMask = dwMask >> 1;
03732       }
03733 
03734       szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
03735 
03736       // Calling GetVolumeInformation on a removeable drive where the
03737       // disk is currently removed will cause a dialog box to the
03738       // console.  This is not good.
03739       // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
03740       // dialog.
03741 
03742       oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
03743 
03744       if (GetVolumeInformation(
03745                   szDrive,
03746                   NULL, 0,
03747                   &dwVolumeSerialNumber,
03748                   NULL, NULL, NULL, 0)
03749          )
03750       {
03751          if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
03752          {
03753             _MD_LOCK(&cachedVolumeLock);
03754             if (dwLastCachedDrive < dwIndex)
03755                dwLastCachedDrive = dwIndex;
03756             dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
03757             _MD_UNLOCK(&cachedVolumeLock);
03758             rv = _PR_LOCAL_FILE;
03759          }
03760       }
03761       if (oldmode != (DWORD) -1) {
03762           SetErrorMode(oldmode);
03763           oldmode = (DWORD) -1;
03764       }
03765 
03766       if (rv == _PR_LOCAL_FILE)
03767           return _PR_LOCAL_FILE;
03768 
03769       dwIndex++;
03770       dwMask = dwMask >> 1;
03771    }
03772 
03773    return _PR_REMOTE_FILE;
03774 }
03775 #endif /* _NEED_351_FILE_LOCKING_HACK */
03776 
03777 PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc *fd)
03778 {
03779     PRThread *me = _PR_MD_CURRENT_THREAD();
03780        PRBool fWait;
03781        PRFileDesc *bottom;
03782 
03783        bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER);
03784     if (!me->io_suspended || (NULL == bottom) ||
03785                                    (me->io_fd != bottom->secret->md.osfd)) {
03786         PR_SetError(PR_INVALID_STATE_ERROR, 0);
03787         return PR_FAILURE;
03788     }
03789        /*
03790         * The CancelIO operation has to be issued by the same NT thread that
03791         * issued the I/O operation
03792         */
03793        PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu));
03794        if (me->io_pending) {
03795               if (!CancelIo((HANDLE)bottom->secret->md.osfd)) {
03796                      PR_SetError(PR_INVALID_STATE_ERROR, GetLastError());
03797                      return PR_FAILURE;
03798               }
03799        }
03800        _PR_THREAD_LOCK(me);
03801        fWait = me->io_pending;
03802        me->io_suspended = PR_FALSE;
03803        me->state = _PR_IO_WAIT;
03804        me->md.interrupt_disabled = PR_TRUE;
03805        _PR_THREAD_UNLOCK(me);
03806        if (fWait)
03807               _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT);
03808        PR_ASSERT(me->io_suspended ==  PR_FALSE);
03809        PR_ASSERT(me->io_pending ==  PR_FALSE);
03810 
03811        _PR_THREAD_LOCK(me);
03812        me->md.interrupt_disabled = PR_FALSE;
03813        me->md.thr_bound_cpu = NULL;
03814     me->io_suspended = PR_FALSE;
03815     me->io_pending = PR_FALSE;
03816        me->state = _PR_RUNNING;
03817        _PR_THREAD_UNLOCK(me);
03818        return PR_SUCCESS;
03819 }
03820 
03821 static PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
03822 {
03823     PRInt32 osfd = fd->secret->md.osfd;
03824     PRInt32 rv, err;
03825     fd_set rd;
03826     struct timeval tv, *tvp;
03827 
03828     FD_ZERO(&rd);
03829     FD_SET((SOCKET)osfd, &rd);
03830     if (timeout == PR_INTERVAL_NO_TIMEOUT) {
03831         while ((rv = accept(osfd, addr, addrlen)) == -1) {
03832             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
03833                     && (!fd->secret->nonblocking)) {
03834                 if ((rv = _PR_NTFiberSafeSelect(osfd + 1, &rd, NULL, NULL,
03835                         NULL)) == -1) {
03836                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
03837                     break;
03838                 }
03839             } else {
03840                 _PR_MD_MAP_ACCEPT_ERROR(err);
03841                 break;
03842             }
03843         }
03844     } else if (timeout == PR_INTERVAL_NO_WAIT) {
03845         if ((rv = accept(osfd, addr, addrlen)) == -1) {
03846             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
03847                     && (!fd->secret->nonblocking)) {
03848                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
03849             } else {
03850                 _PR_MD_MAP_ACCEPT_ERROR(err);
03851             }
03852         }
03853     } else {
03854 retry:
03855         if ((rv = accept(osfd, addr, addrlen)) == -1) {
03856             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
03857                     && (!fd->secret->nonblocking)) {
03858                 tv.tv_sec = PR_IntervalToSeconds(timeout);
03859                 tv.tv_usec = PR_IntervalToMicroseconds(
03860                     timeout - PR_SecondsToInterval(tv.tv_sec));
03861                 tvp = &tv;
03862 
03863                 rv = _PR_NTFiberSafeSelect(osfd + 1, &rd, NULL, NULL, tvp);
03864                 if (rv > 0) {
03865                     goto retry;
03866                 } else if (rv == 0) {
03867                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
03868                     rv = -1;
03869                 } else {
03870                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
03871                 }
03872             } else {
03873                 _PR_MD_MAP_ACCEPT_ERROR(err);
03874             }
03875         }
03876     }
03877     return(rv);
03878 }
03879 
03880 static PRInt32 _nt_nonblock_connect(PRFileDesc *fd, struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
03881 {
03882     PRInt32 osfd = fd->secret->md.osfd;
03883     PRInt32 rv;
03884     int err;
03885     fd_set wr, ex;
03886     struct timeval tv, *tvp;
03887     int len;
03888 
03889     if ((rv = connect(osfd, addr, addrlen)) == -1) {
03890         if ((err = WSAGetLastError()) == WSAEWOULDBLOCK) {
03891             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
03892                 tvp = NULL;
03893             } else {
03894                 tv.tv_sec = PR_IntervalToSeconds(timeout);
03895                 tv.tv_usec = PR_IntervalToMicroseconds(
03896                     timeout - PR_SecondsToInterval(tv.tv_sec));
03897                 tvp = &tv;
03898             }
03899             FD_ZERO(&wr);
03900             FD_ZERO(&ex);
03901             FD_SET((SOCKET)osfd, &wr);
03902             FD_SET((SOCKET)osfd, &ex);
03903             if ((rv = _PR_NTFiberSafeSelect(osfd + 1, NULL, &wr, &ex,
03904                     tvp)) == -1) {
03905                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
03906                 return rv;
03907             }
03908             if (rv == 0) {
03909                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
03910                 return -1;
03911             }
03912             /* Call Sleep(0) to work around a Winsock timeing bug. */
03913             Sleep(0);
03914             if (FD_ISSET((SOCKET)osfd, &ex)) {
03915                 len = sizeof(err);
03916                 if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
03917                         (char *) &err, &len) == SOCKET_ERROR) {
03918                     _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
03919                     return -1;
03920                 }
03921                 _PR_MD_MAP_CONNECT_ERROR(err);
03922                 return -1;
03923             } 
03924             PR_ASSERT(FD_ISSET((SOCKET)osfd, &wr));
03925             rv = 0;
03926         } else {
03927             _PR_MD_MAP_CONNECT_ERROR(err);
03928         }
03929     }
03930     return rv;
03931 }
03932 
03933 static PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, int flags, PRIntervalTime timeout)
03934 {
03935     PRInt32 osfd = fd->secret->md.osfd;
03936     PRInt32 rv, err;
03937     struct timeval tv, *tvp;
03938     fd_set rd;
03939     int osflags;
03940 
03941     if (0 == flags) {
03942         osflags = 0;
03943     } else {
03944         PR_ASSERT(PR_MSG_PEEK == flags);
03945         osflags = MSG_PEEK;
03946     }
03947     while ((rv = recv(osfd,buf,len,osflags)) == -1) {
03948         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
03949                 && (!fd->secret->nonblocking)) {
03950             FD_ZERO(&rd);
03951             FD_SET((SOCKET)osfd, &rd);
03952             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
03953                 tvp = NULL;
03954             } else {
03955                 tv.tv_sec = PR_IntervalToSeconds(timeout);
03956                 tv.tv_usec = PR_IntervalToMicroseconds(
03957                 timeout - PR_SecondsToInterval(tv.tv_sec));
03958                 tvp = &tv;
03959             }
03960             if ((rv = _PR_NTFiberSafeSelect(osfd + 1, &rd, NULL, NULL,
03961                     tvp)) == -1) {
03962                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
03963                 break;
03964             } else if (rv == 0) {
03965                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
03966                 rv = -1;
03967                 break;
03968             }
03969         } else {
03970             _PR_MD_MAP_RECV_ERROR(err);
03971             break;
03972         }
03973     }
03974     return(rv);
03975 }
03976 
03977 static PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
03978 {
03979     PRInt32 osfd = fd->secret->md.osfd;
03980     PRInt32 rv, err;
03981     struct timeval tv, *tvp;
03982     fd_set wd;
03983     PRInt32 bytesSent = 0;
03984 
03985     while(bytesSent < len) {
03986         while ((rv = send(osfd,buf,len,0)) == -1) {
03987             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
03988                     && (!fd->secret->nonblocking)) {
03989                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
03990                     tvp = NULL;
03991                 } else {
03992                     tv.tv_sec = PR_IntervalToSeconds(timeout);
03993                     tv.tv_usec = PR_IntervalToMicroseconds(
03994                         timeout - PR_SecondsToInterval(tv.tv_sec));
03995                     tvp = &tv;
03996                 }
03997                 FD_ZERO(&wd);
03998                 FD_SET((SOCKET)osfd, &wd);
03999                 if ((rv = _PR_NTFiberSafeSelect(osfd + 1, NULL, &wd, NULL,
04000                         tvp)) == -1) {
04001                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
04002                     return -1;
04003                 }
04004                 if (rv == 0) {
04005                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
04006                     return -1;
04007                 }
04008             } else {
04009                 _PR_MD_MAP_SEND_ERROR(err);
04010                 return -1;
04011             }
04012         }
04013         bytesSent += rv;
04014         if (fd->secret->nonblocking) {
04015             break;
04016         }
04017         if (bytesSent < len) {
04018             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
04019                 tvp = NULL;
04020             } else {
04021                 tv.tv_sec = PR_IntervalToSeconds(timeout);
04022                 tv.tv_usec = PR_IntervalToMicroseconds(
04023                     timeout - PR_SecondsToInterval(tv.tv_sec));
04024                 tvp = &tv;
04025             }
04026             FD_ZERO(&wd);
04027             FD_SET((SOCKET)osfd, &wd);
04028             if ((rv = _PR_NTFiberSafeSelect(osfd + 1, NULL, &wd, NULL,
04029                     tvp)) == -1) {
04030                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
04031                 return -1;
04032             }
04033             if (rv == 0) {
04034                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
04035                 return -1;
04036             }
04037         }
04038     }
04039     return bytesSent;
04040 }
04041 
04042 static PRInt32 _nt_nonblock_writev(PRFileDesc *fd, const PRIOVec *iov, int size, PRIntervalTime timeout)
04043 {
04044     int index;
04045     int sent = 0;
04046     int rv;
04047 
04048     for (index=0; index<size; index++) {
04049         rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
04050         if (rv > 0) 
04051             sent += rv;
04052         if ( rv != iov[index].iov_len ) {
04053             if (rv < 0) {
04054                 if (fd->secret->nonblocking
04055                         && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
04056                         && (sent > 0)) {
04057                     return sent;
04058                 } else {
04059                     return -1;
04060                 }
04061             }
04062             /* Only a nonblocking socket can have partial sends */
04063             PR_ASSERT(fd->secret->nonblocking);
04064             return sent;
04065         }
04066     }
04067 
04068     return sent;
04069 }
04070 
04071 static PRInt32 _nt_nonblock_sendto(
04072     PRFileDesc *fd, const char *buf, int len,
04073     const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
04074 {
04075     PRInt32 osfd = fd->secret->md.osfd;
04076     PRInt32 rv, err;
04077     struct timeval tv, *tvp;
04078     fd_set wd;
04079     PRInt32 bytesSent = 0;
04080 
04081     while(bytesSent < len) {
04082         while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
04083             if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
04084                     && (!fd->secret->nonblocking)) {
04085                 if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
04086                     tvp = NULL;
04087                 } else {
04088                     tv.tv_sec = PR_IntervalToSeconds(timeout);
04089                     tv.tv_usec = PR_IntervalToMicroseconds(
04090                         timeout - PR_SecondsToInterval(tv.tv_sec));
04091                     tvp = &tv;
04092                 }
04093                 FD_ZERO(&wd);
04094                 FD_SET((SOCKET)osfd, &wd);
04095                 if ((rv = _PR_NTFiberSafeSelect(osfd + 1, NULL, &wd, NULL,
04096                         tvp)) == -1) {
04097                     _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
04098                     return -1;
04099                 }
04100                 if (rv == 0) {
04101                     PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
04102                     return -1;
04103                 }
04104             } else {
04105                 _PR_MD_MAP_SENDTO_ERROR(err);
04106                 return -1;
04107             }
04108         }
04109         bytesSent += rv;
04110         if (fd->secret->nonblocking) {
04111             break;
04112         }
04113         if (bytesSent < len) {
04114             if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
04115                 tvp = NULL;
04116             } else {
04117                 tv.tv_sec = PR_IntervalToSeconds(timeout);
04118                 tv.tv_usec = PR_IntervalToMicroseconds(
04119                     timeout - PR_SecondsToInterval(tv.tv_sec));
04120                 tvp = &tv;
04121             }
04122             FD_ZERO(&wd);
04123             FD_SET((SOCKET)osfd, &wd);
04124             if ((rv = _PR_NTFiberSafeSelect(osfd + 1, NULL, &wd, NULL,
04125                     tvp)) == -1) {
04126                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
04127                 return -1;
04128             }
04129             if (rv == 0) {
04130                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
04131                 return -1;
04132             }
04133         }
04134     }
04135     return bytesSent;
04136 }
04137 
04138 static PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
04139 {
04140     PRInt32 osfd = fd->secret->md.osfd;
04141     PRInt32 rv, err;
04142     struct timeval tv, *tvp;
04143     fd_set rd;
04144 
04145     while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
04146         if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
04147                 && (!fd->secret->nonblocking)) {
04148             if (timeout == PR_INTERVAL_NO_TIMEOUT) {
04149                 tvp = NULL;
04150             } else {
04151                 tv.tv_sec = PR_IntervalToSeconds(timeout);
04152                 tv.tv_usec = PR_IntervalToMicroseconds(
04153                 timeout - PR_SecondsToInterval(tv.tv_sec));
04154                 tvp = &tv;
04155             }
04156             FD_ZERO(&rd);
04157             FD_SET((SOCKET)osfd, &rd);
04158             if ((rv = _PR_NTFiberSafeSelect(osfd + 1, &rd, NULL, NULL,
04159                     tvp)) == -1) {
04160                 _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
04161                 break;
04162             } else if (rv == 0) {
04163                 PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
04164                 rv = -1;
04165                 break;
04166             }
04167         } else {
04168             _PR_MD_MAP_RECVFROM_ERROR(err);
04169             break;
04170         }
04171     }
04172     return(rv);
04173 }
04174 
04175 /*
04176  * UDP support: the continuation thread functions and recvfrom and sendto.
04177  */
04178 
04179 static void pt_InsertTimedInternal(pt_Continuation *op)
04180 {
04181     PRInt32 delta = 0;
04182     pt_Continuation *t_op = NULL;
04183     PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
04184 
04185     /*
04186      * If this element operation isn't timed, it gets queued at the
04187      * end of the list (just after pt_tq.tail) and we're
04188      * finishd early.
04189      */
04190     if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
04191     {
04192         t_op = pt_tq.tail;  /* put it at the end */
04193         goto done;
04194     }
04195 
04196     /*
04197      * The rest of this routine actaully deals with timed ops.
04198      */
04199 
04200     if (NULL != pt_tq.op)
04201     {
04202         /*
04203          * To find where in the list to put the new operation, form
04204          * the absolute time the operations in question will expire.
04205          *
04206          * The new operation ('op') will expire at now() + op->timeout.
04207          *
04208          * The operation that will time out furthest in the future will
04209          * do so at pt_tq.epoch + pt_tq.op->timeout.
04210          *
04211          * Subsequently earlier timeouts are computed based on the latter
04212          * knowledge by subracting the timeout deltas that are stored in
04213          * the operation list. There are operation[n]->timeout ticks
04214          * between the expiration of operation[n-1] and operation[n].e e 
04215          *
04216          * Therefore, the operation[n-1] will expire operation[n]->timeout
04217          * ticks prior to operation[n].
04218          *
04219          * This should be easy!
04220          */
04221         t_op = pt_tq.op;  /* running pointer to queued op */
04222         op_tmo = now + op->timeout;  /* that's in absolute ticks */
04223         qd_tmo = pt_tq.epoch + t_op->timeout;  /* likewise */
04224 
04225         do
04226         {
04227             /*
04228              * If 'op' expires later than t_op, then insert 'op' just
04229              * ahead of t_op. Otherwise, compute when operation[n-1]
04230              * expires and try again.
04231              *
04232              * The actual different between the expiriation of 'op'
04233              * and the current operation what becomes the new operaton's
04234              * timeout interval. That interval is also subtracted from
04235              * the interval of the operation immediately following where
04236              * we stick 'op' (unless the next one isn't timed). The new
04237              * timeout assigned to 'op' takes into account the values of
04238              * now() and when the previous intervals were compured.
04239              */
04240             delta = op_tmo - qd_tmo;
04241             if (delta >= 0)
04242             {
04243                 op->timeout += (now - pt_tq.epoch);
04244                 goto done;
04245             }
04246 
04247             qd_tmo -= t_op->timeout;  /* previous operaton expiration */
04248             t_op = t_op->prev;  /* point to previous operation */
04249             if (NULL != t_op) qd_tmo += t_op->timeout;
04250         } while (NULL != t_op);
04251 
04252         /*
04253          * If we got here we backed off the head of the list. That means that
04254          * this timed entry has to go at the head of the list. This is just
04255          * about like having an empty timer list.
04256          */
04257         delta = op->timeout;  /* $$$ is this right? */
04258     }
04259 
04260 done:
04261 
04262     /*
04263      * Insert 'op' into the queue just after t_op or if t_op is null,
04264      * at the head of the list.
04265      *
04266      * If t_op is NULL, the list is currently empty and this is pretty
04267      * easy.
04268      */
04269     if (NULL == t_op)
04270     {
04271         op->prev = NULL;
04272         op->next = pt_tq.head;
04273         pt_tq.head = op;
04274         if (NULL == pt_tq.tail) pt_tq.tail = op;
04275         else op->next->prev = op;
04276     }
04277     else
04278     {
04279         op->prev = t_op;
04280         op->next = t_op->next;
04281         if (NULL != op->prev)
04282             op->prev->next = op;
04283         if (NULL != op->next)
04284             op->next->prev = op;
04285         if (t_op == pt_tq.tail)
04286             pt_tq.tail = op;
04287     }
04288 
04289     /*
04290      * Are we adjusting our epoch, etc? Are we replacing
04291      * what was previously the element due to expire furthest
04292      * out in the future? Is this even a timed operation?
04293      */
04294     if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
04295     {
04296         if ((NULL == pt_tq.op)  /* we're the one and only */
04297         || (t_op == pt_tq.op))  /* we're replacing */
04298         {
04299             pt_tq.op = op;
04300             pt_tq.epoch = now;
04301         }
04302     }
04303 
04304     pt_tq.op_count += 1;
04305 
04306 }  /* pt_InsertTimedInternal */
04307 
04308 /*
04309  * function: pt_FinishTimed
04310  *
04311  * Takes the finished operation out of the timed queue. It
04312  * notifies the initiating thread that the opertions is
04313  * complete and returns to the caller the value of the next
04314  * operation in the list (or NULL).
04315  */
04316 static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
04317 {
04318     pt_Continuation *next;
04319 
04320     /* remove this one from the list */
04321     if (NULL == op->prev) pt_tq.head = op->next;
04322     else op->prev->next = op->next;
04323     if (NULL == op->next) pt_tq.tail = op->prev;
04324     else op->next->prev = op->prev;
04325 
04326     /* did we happen to hit the timed op? */
04327     if (op == pt_tq.op) pt_tq.op = op->prev;
04328 
04329     next = op->next;
04330     op->next = op->prev = NULL;
04331     op->status = pt_continuation_done;
04332 
04333     pt_tq.op_count -= 1;
04334 #if defined(DEBUG)
04335     pt_debug.continuationsServed += 1;
04336 #endif
04337     PR_NotifyCondVar(op->complete);
04338 
04339     return next;
04340 }  /* pt_FinishTimedInternal */
04341 
04342 static void ContinuationThread(void *arg)
04343 {
04344     /* initialization */
04345     fd_set readSet, writeSet, exceptSet;
04346     struct timeval tv;
04347     SOCKET *pollingList = 0;                /* list built for polling */
04348     PRIntn pollingListUsed;                 /* # entries used in the list */
04349     PRIntn pollingListNeeded;               /* # entries needed this time */
04350     PRIntn pollingSlotsAllocated = 0;       /* # entries available in list */
04351     PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
04352 
04353     /* do some real work */
04354     while (1)
04355     {
04356         PRIntn rv;
04357         PRStatus status;
04358         PRIntn pollIndex;
04359         pt_Continuation *op;
04360         PRIntervalTime now = PR_IntervalNow();
04361         PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
04362 
04363         PR_Lock(pt_tq.ml);
04364         while (NULL == pt_tq.head)
04365         {
04366             status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
04367             if ((PR_FAILURE == status)
04368                 && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
04369         }
04370         pollingListNeeded = pt_tq.op_count;
04371         PR_Unlock(pt_tq.ml);
04372 
04373         /* Okay. We're history */
04374         if ((PR_FAILURE == status)
04375             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
04376 
04377        /*
04378         * We are not holding the pt_tq.ml lock now, so more items may
04379         * get added to pt_tq during this window of time.  We hope
04380         * that 10 more spaces in the polling list should be enough.
04381         */
04382 
04383         FD_ZERO(&readSet);
04384         FD_ZERO(&writeSet);
04385         FD_ZERO(&exceptSet);
04386         pollingListNeeded += 10;
04387         if (pollingListNeeded > pollingSlotsAllocated)
04388         {
04389             if (NULL != pollingList) PR_DELETE(pollingList);
04390             pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
04391             PR_ASSERT(NULL != pollingList);
04392             pollingSlotsAllocated = pollingListNeeded;
04393         }
04394 
04395 #if defined(DEBUG)
04396         if (pollingListNeeded > pt_debug.pollingListMax)
04397             pt_debug.pollingListMax = pollingListUsed;
04398 #endif
04399 
04400         /*
04401          * Build up a polling list.
04402          * This list is sorted on time. Operations that have been
04403          * interrupted are completed and not included in the list.
04404          * There is an assertion that the operation is in progress.
04405          */
04406         pollingListUsed = 0;
04407         PR_Lock(pt_tq.ml);
04408 
04409         for (op = pt_tq.head; NULL != op;)
04410         {
04411             if (pt_continuation_abort == op->status)
04412             {
04413                 op->result.code = -1;
04414                 op->syserrno = WSAEINTR;
04415                 op = pt_FinishTimedInternal(op);
04416             }
04417             else
04418             {
04419                 PR_ASSERT(pt_continuation_done != op->status);
04420                 op->status = pt_continuation_inprogress;
04421                 if (op->event & PR_POLL_READ) {
04422                     FD_SET(op->arg1.osfd, &readSet);
04423                 }
04424                 if (op->event & PR_POLL_WRITE) {
04425                     FD_SET(op->arg1.osfd, &writeSet);
04426                 }
04427                 if (op->event & PR_POLL_EXCEPT) {
04428                     FD_SET(op->arg1.osfd, &exceptSet);
04429                 }
04430                 pollingList[pollingListUsed] = op->arg1.osfd;
04431                 pollingListUsed += 1;
04432                 if (pollingListUsed == pollingSlotsAllocated) break;
04433                 op = op->next;
04434             }
04435         }
04436 
04437         PR_Unlock(pt_tq.ml);
04438 
04439         /*
04440          * If 'op' isn't NULL at this point, then we didn't get to
04441          * the end of the list. That means that more items got added
04442          * to the list than we anticipated. So, forget this iteration,
04443          * go around the horn again.
04444          * One would hope this doesn't happen all that often.
04445          */
04446         if (NULL != op)
04447         {
04448 #if defined(DEBUG)
04449             pt_debug.predictionsFoiled += 1;  /* keep track */
04450 #endif
04451             continue;  /* make it rethink things */
04452         }
04453 
04454         /* there's a chance that all ops got blown away */
04455         if (NULL == pt_tq.head) continue;
04456         /* if not, we know this is the shortest timeout */
04457         timeout = pt_tq.head->timeout;
04458 
04459         /*
04460          * We don't want to wait forever on this poll. So keep
04461          * the interval down. The operations, if they are timed,
04462          * still have to timeout, while those that are not timed
04463          * should persist forever. But they may be aborted. That's
04464          * what this anxiety is all about.
04465          */
04466         if (timeout > mx_select_ticks) timeout = mx_select_ticks;
04467 
04468         if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)
04469             pt_tq.head->timeout -= timeout;
04470         tv.tv_sec = PR_IntervalToSeconds(timeout);
04471         tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
04472 
04473         rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
04474 
04475         if (0 == rv)  /* poll timed out - what about leading op? */
04476         {
04477             if (0 == pt_tq.head->timeout)
04478             {
04479                 /* 
04480                  * The leading element of the timed queue has timed
04481                  * out. Get rid of it. In any case go around the
04482                  * loop again, computing the polling list, checking
04483                  * for interrupted operations.
04484                  */
04485                 PR_Lock(pt_tq.ml);
04486                 do
04487                 {
04488                     pt_tq.head->result.code = -1;
04489                     pt_tq.head->syserrno = WSAETIMEDOUT;
04490                     op = pt_FinishTimedInternal(pt_tq.head);
04491                 } while ((NULL != op) && (0 == op->timeout));
04492                 PR_Unlock(pt_tq.ml);
04493             }
04494             continue;
04495         }
04496 
04497         if (-1 == rv && (WSAGetLastError() == WSAEINTR
04498                 || WSAGetLastError() == WSAEINPROGRESS))
04499         {
04500             continue;               /* go around the loop again */
04501         }
04502 
04503         /*
04504          * select() says that something in our list is ready for some more
04505          * action or is an invalid fd. Find it, load up the operation and
04506          * see what happens.
04507          */
04508 
04509         PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
04510 
04511 
04512         /*
04513          * $$$ There's a problem here. I'm running the operations list
04514          * and I'm not holding any locks. I don't want to hold the lock
04515          * and do the operation, so this is really messed up..
04516          *
04517          * This may work out okay. The rule is that only this thread,
04518          * the continuation thread, can remove elements from the list.
04519          * Therefore, the list is at worst, longer than when we built
04520          * the polling list.
04521          */
04522         op = pt_tq.head;
04523         for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
04524         {
04525             PRInt16 revents = 0;
04526 
04527             PR_ASSERT(NULL != op);
04528 
04529             /*
04530              * This one wants attention. Redo the operation.
04531              * We know that there can only be more elements
04532              * in the op list than we knew about when we created
04533              * the poll list. Therefore, we might have to skip
04534              * a few ops to find the right one to operation on.
04535              */
04536             while (pollingList[pollIndex] != op->arg1.osfd )
04537             {
04538                 op = op->next;
04539                 PR_ASSERT(NULL != op);
04540             }
04541 
04542             if (FD_ISSET(op->arg1.osfd, &readSet)) {
04543                 revents |= PR_POLL_READ;
04544             }
04545             if (FD_ISSET(op->arg1.osfd, &writeSet)) {
04546                 revents |= PR_POLL_WRITE;
04547             }
04548             if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
04549                 revents |= PR_POLL_EXCEPT;
04550             }
04551 
04552             /*
04553              * Sip over all those not in progress. They'll be
04554              * pruned next time we build a polling list. Call
04555              * the continuation function. If it reports completion,
04556              * finish off the operation.
04557              */
04558             if (revents && (pt_continuation_inprogress == op->status)
04559                 && (op->function(op, revents)))
04560             {
04561                 PR_Lock(pt_tq.ml);
04562                 op = pt_FinishTimedInternal(op);
04563                 PR_Unlock(pt_tq.ml);
04564             }
04565         }
04566     }
04567     if (NULL != pollingList) PR_DELETE(pollingList);
04568 }  /* ContinuationThread */
04569 
04570 static int pt_Continue(pt_Continuation *op)
04571 {
04572     PRStatus rv;
04573     /* Finish filling in the blank slots */
04574     op->status = pt_continuation_sumbitted;
04575     op->complete = PR_NewCondVar(pt_tq.ml);
04576 
04577     PR_Lock(pt_tq.ml);  /* we provide the locking */
04578 
04579     pt_InsertTimedInternal(op);  /* insert in the structure */
04580 
04581     PR_NotifyCondVar(pt_tq.new_op);  /* notify the continuation thread */
04582 
04583     while (pt_continuation_done != op->status)  /* wait for completion */
04584     {
04585         rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
04586         /*
04587          * If we get interrupted, we set state the continuation thread will
04588          * see and allow it to finish the I/O operation w/ error. That way
04589          * the rule that only the continuation thread is removing elements
04590          * from the list is still valid.
04591          *
04592          * Don't call interrupt on the continuation thread. That'll just
04593          * piss him off. He's cycling around at least every mx_select_ticks
04594          * anyhow and should notice the request in there.
04595          */
04596         if ((PR_FAILURE == rv)
04597             && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
04598             op->status = pt_continuation_abort;  /* our status */
04599     }
04600 
04601     PR_Unlock(pt_tq.ml);  /* we provide the locking */
04602 
04603     PR_DestroyCondVar(op->complete);
04604 
04605     return op->result.code;  /* and the primary answer */
04606 }  /* pt_Continue */
04607 
04608 static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
04609 {
04610     PRIntn bytes = sendto(
04611         op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
04612         (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
04613     op->syserrno = WSAGetLastError();
04614     if (bytes > 0)  /* this is progress */
04615     {
04616         char *bp = op->arg2.buffer;
04617         bp += bytes;  /* adjust the buffer pointer */
04618         op->arg2.buffer = bp;
04619         op->result.code += bytes;  /* accumulate the number sent */
04620         op->arg3.amount -= bytes;  /* and reduce the required count */
04621         return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
04622     }
04623     else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
04624        PR_FALSE : PR_TRUE;
04625 }  /* pt_sendto_cont */
04626 
04627 static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
04628 {
04629     PRIntn addr_len = sizeof(*(op->arg5.addr));
04630     op->result.code = recvfrom(
04631         op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
04632         op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
04633     op->syserrno = WSAGetLastError();
04634     return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
04635         PR_FALSE : PR_TRUE;
04636 }  /* pt_recvfrom_cont */
04637 
04638 static PRInt32 pt_SendTo(
04639     SOCKET osfd, const void *buf,
04640     PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
04641     PRIntn addrlen, PRIntervalTime timeout)
04642 {
04643     PRInt32 bytes = -1, err;
04644     PRBool fNeedContinue = PR_FALSE;
04645 
04646     bytes = sendto(
04647             osfd, buf, amount, flags,
04648             (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
04649     if (bytes == -1) {
04650               if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
04651         fNeedContinue = PR_TRUE;
04652               else
04653                      _PR_MD_MAP_SENDTO_ERROR(err);
04654     }
04655     if (fNeedContinue == PR_TRUE)
04656     {
04657         pt_Continuation op;
04658         op.arg1.osfd = osfd;
04659         op.arg2.buffer = (void*)buf;
04660         op.arg3.amount = amount;
04661         op.arg4.flags = flags;
04662         op.arg5.addr = (PRNetAddr*)addr;
04663         op.timeout = timeout;
04664         op.result.code = 0;  /* initialize the number sent */
04665         op.function = pt_sendto_cont;
04666         op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
04667         bytes = pt_Continue(&op);
04668         if (bytes < 0) {
04669             WSASetLastError(op.syserrno);
04670                      _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
04671         }
04672     }
04673     return bytes;
04674 }  /* pt_SendTo */
04675 
04676 static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
04677     PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
04678 {
04679     PRInt32 bytes = -1, err;
04680     PRBool fNeedContinue = PR_FALSE;
04681 
04682     bytes = recvfrom(
04683             osfd, buf, amount, flags,
04684             (struct sockaddr*)addr, addr_len);
04685     if (bytes == -1) {
04686               if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
04687         fNeedContinue = PR_TRUE;
04688               else
04689                      _PR_MD_MAP_RECVFROM_ERROR(err);
04690     }
04691 
04692     if (fNeedContinue == PR_TRUE)
04693     {
04694         pt_Continuation op;
04695         op.arg1.osfd = osfd;
04696         op.arg2.buffer = buf;
04697         op.arg3.amount = amount;
04698         op.arg4.flags = flags;
04699         op.arg5.addr = addr;
04700         op.timeout = timeout;
04701         op.function = pt_recvfrom_cont;
04702         op.event = PR_POLL_READ | PR_POLL_EXCEPT;
04703         bytes = pt_Continue(&op);
04704         if (bytes < 0) {
04705             WSASetLastError(op.syserrno);
04706                      _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
04707         }
04708     }
04709     return bytes;
04710 }  /* pt_RecvFrom */