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