summaryrefslogtreecommitdiff
path: root/pr/src/md/windows
diff options
context:
space:
mode:
Diffstat (limited to 'pr/src/md/windows')
-rw-r--r--pr/src/md/windows/Makefile75
-rw-r--r--pr/src/md/windows/ntgc.c107
-rw-r--r--pr/src/md/windows/ntinrval.c86
-rw-r--r--pr/src/md/windows/ntio.c3776
-rw-r--r--pr/src/md/windows/ntmisc.c603
-rw-r--r--pr/src/md/windows/ntsem.c65
-rw-r--r--pr/src/md/windows/ntthread.c434
-rw-r--r--pr/src/md/windows/w16callb.c243
-rw-r--r--pr/src/md/windows/w16error.c233
-rw-r--r--pr/src/md/windows/w16fmem.c59
-rw-r--r--pr/src/md/windows/w16gc.c67
-rw-r--r--pr/src/md/windows/w16io.c836
-rw-r--r--pr/src/md/windows/w16mem.c66
-rw-r--r--pr/src/md/windows/w16null.c97
-rw-r--r--pr/src/md/windows/w16proc.c58
-rw-r--r--pr/src/md/windows/w16sock.c1151
-rw-r--r--pr/src/md/windows/w16stdio.c150
-rw-r--r--pr/src/md/windows/w16thred.c407
-rw-r--r--pr/src/md/windows/w32poll.c189
-rw-r--r--pr/src/md/windows/w95cv.c328
-rw-r--r--pr/src/md/windows/w95io.c871
-rw-r--r--pr/src/md/windows/w95sock.c614
-rw-r--r--pr/src/md/windows/w95thred.c226
-rw-r--r--pr/src/md/windows/win32_errors.c1104
24 files changed, 11845 insertions, 0 deletions
diff --git a/pr/src/md/windows/Makefile b/pr/src/md/windows/Makefile
new file mode 100644
index 00000000..eaac7b56
--- /dev/null
+++ b/pr/src/md/windows/Makefile
@@ -0,0 +1,75 @@
+#
+# The contents of this file are subject to the Netscape Public License
+# Version 1.0 (the "NPL"); you may not use this file except in
+# compliance with the NPL. You may obtain a copy of the NPL at
+# http://www.mozilla.org/NPL/
+#
+# Software distributed under the NPL is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+# for the specific language governing rights and limitations under the
+# NPL.
+#
+# The Initial Developer of this code under the NPL is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1998 Netscape Communications Corporation. All Rights
+# Reserved.
+#
+
+#! gmake
+
+MOD_DEPTH = ../../../..
+
+include $(MOD_DEPTH)/config/config.mk
+
+ifeq ($(OS_TARGET), WIN16)
+CSRCS = \
+ w16null.c \
+ w16thred.c \
+ w16proc.c \
+ w16fmem.c \
+ w16sock.c \
+ w16mem.c \
+ w16io.c \
+ w16gc.c \
+ w16error.c \
+ w16stdio.c \
+ w16callb.c \
+ ntinrval.c \
+ $(NULL)
+else
+ifeq ($(OS_TARGET), WIN95)
+CSRCS = \
+ ntmisc.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ w95thred.c \
+ w95io.c \
+ w95cv.c \
+ w95sock.c \
+ win32_errors.c \
+ w32poll.c \
+ $(NULL)
+else
+CSRCS = \
+ ntmisc.c \
+ ntsem.c \
+ ntinrval.c \
+ ntgc.c \
+ ntthread.c \
+ ntio.c \
+ win32_errors.c \
+ w32poll.c \
+ $(NULL)
+endif
+endif
+
+TARGETS = $(OBJS)
+
+INCLUDES = -I$(DIST)/include/private -I$(DIST)/include
+
+include $(MOD_DEPTH)/config/rules.mk
+
+export:: $(TARGETS)
+
+install:: export
diff --git a/pr/src/md/windows/ntgc.c b/pr/src/md/windows/ntgc.c
new file mode 100644
index 00000000..8bc21dba
--- /dev/null
+++ b/pr/src/md/windows/ntgc.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * GC related routines
+ *
+ */
+#include <windows.h>
+#include "primpl.h"
+
+PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+#if defined(_X86_)
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_INTEGER;
+
+ if (_PR_IS_NATIVE_THREAD(t)) {
+ context.ContextFlags |= CONTEXT_CONTROL;
+ if (GetThreadContext(t->md.handle, &context)) {
+ t->md.gcContext[0] = context.Eax;
+ t->md.gcContext[1] = context.Ebx;
+ t->md.gcContext[2] = context.Ecx;
+ t->md.gcContext[3] = context.Edx;
+ t->md.gcContext[4] = context.Esi;
+ t->md.gcContext[5] = context.Edi;
+ t->md.gcContext[6] = context.Esp;
+ t->md.gcContext[7] = context.Ebp;
+ *np = PR_NUM_GCREGS;
+ } else {
+ PR_ASSERT(0);/* XXX */
+ }
+ } else {
+ /* WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * This code is extremely machine dependant and completely
+ * undocumented by MS. Its only known to work experimentally.
+ * Ready for a walk on the wild * side?
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
+
+#if !defined WIN95 // Win95 does not have fibers
+ int *fiberData = t->md.fiber_id;
+
+ /* I found these offsets by disassembling SwitchToFiber().
+ * Are your palms sweating yet?
+ */
+
+ /*
+ ** EAX is on the stack (ESP+0)
+ ** EDX is on the stack (ESP+4)
+ ** ECX is on the stack (ESP+8)
+ */
+ t->md.gcContext[0] = 0; /* context.Eax */
+ t->md.gcContext[1] = fiberData[0x2e]; /* context.Ebx */
+ t->md.gcContext[2] = 0; /* context.Ecx */
+ t->md.gcContext[2] = 0; /* context.Edx */
+ t->md.gcContext[4] = fiberData[0x2d]; /* context.Esi */
+ t->md.gcContext[5] = fiberData[0x2c]; /* context.Edi */
+ t->md.gcContext[6] = fiberData[0x36]; /* context.Esp */
+ t->md.gcContext[7] = fiberData[0x32]; /* context.Ebp */
+ *np = PR_NUM_GCREGS;
+#endif
+ }
+ return (PRWord *)&t->md.gcContext;
+#elif defined(_ALPHA_)
+#endif /* defined(_X86_) */
+}
+
+/* This function is not used right now, but is left as a reference.
+ * If you ever need to get the fiberID from the currently running fiber,
+ * this is it.
+ */
+void *
+GetMyFiberID()
+{
+#if defined(_X86_)
+ void *fiberData;
+
+ /* A pointer to our tib entry is found at FS:[18]
+ * At offset 10h is the fiberData pointer. The context of the
+ * fiber is stored in there.
+ */
+ __asm {
+ mov EDX, FS:[18h]
+ mov EAX, DWORD PTR [EDX+10h]
+ mov [fiberData], EAX
+ }
+
+ return fiberData;
+#elif defined(_ALPHA_)
+#endif /* defined(_X86_) */
+}
diff --git a/pr/src/md/windows/ntinrval.c b/pr/src/md/windows/ntinrval.c
new file mode 100644
index 00000000..05cc3ae1
--- /dev/null
+++ b/pr/src/md/windows/ntinrval.c
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * NT interval timers
+ *
+ */
+
+#include "primpl.h"
+
+#if defined(WIN16)
+#include <win/compobj.h>
+#define QueryPerformanceFrequency(x) FALSE
+#define QueryPerformanceCounter(x) FALSE
+#endif
+
+PRIntn _nt_bitShift = 0;
+PRInt32 _nt_highMask = 0;
+PRInt32 _nt_ticksPerSec = -1;
+
+void
+_PR_MD_INTERVAL_INIT()
+{
+ LARGE_INTEGER count;
+
+ if (QueryPerformanceFrequency(&count)) {
+ while(count.LowPart > PR_INTERVAL_MAX) {
+ count.LowPart >>= 1;
+ _nt_bitShift++;
+ _nt_highMask = (_nt_highMask << 1)+1;
+ }
+
+ _nt_ticksPerSec = count.LowPart;
+ PR_ASSERT(_nt_ticksPerSec > PR_INTERVAL_MIN);
+ } else
+ _nt_ticksPerSec = -1;
+}
+
+PRIntervalTime
+_PR_MD_GET_INTERVAL()
+{
+ LARGE_INTEGER count;
+
+ /* Sadly; nspr requires the interval to range from 1000 ticks per second
+ * to only 100000 ticks per second; QueryPerformanceCounter is too high
+ * resolution...
+ */
+ if (QueryPerformanceCounter(&count)) {
+ PRInt32 top = count.HighPart & _nt_highMask;
+ top = top << (32 - _nt_bitShift);
+ count.LowPart = count.LowPart >> _nt_bitShift;
+ count.LowPart = count.LowPart + top;
+ return (PRUint32)count.LowPart;
+ } else
+#if defined(WIN16)
+ return clock(); /* milliseconds since application start */
+#else
+ return timeGetTime(); /* milliseconds since system start */
+#endif
+}
+
+PRIntervalTime
+_PR_MD_INTERVAL_PER_SEC()
+{
+ LARGE_INTEGER count;
+
+ if (_nt_ticksPerSec != -1)
+ return _nt_ticksPerSec;
+ else
+ return 1000;
+}
diff --git a/pr/src/md/windows/ntio.c b/pr/src/md/windows/ntio.c
new file mode 100644
index 00000000..e9c5779c
--- /dev/null
+++ b/pr/src/md/windows/ntio.c
@@ -0,0 +1,3776 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Windows NT IO module
+ *
+ * This module handles IO for LOCAL_SCOPE and GLOBAL_SCOPE threads.
+ * For LOCAL_SCOPE threads, we're using NT fibers. For GLOBAL_SCOPE threads
+ * we're using NT-native threads.
+ *
+ * When doing IO, we want to use completion ports for optimal performance
+ * with fibers. But if we use completion ports for all IO, it is difficult
+ * to project a blocking model with GLOBAL_SCOPE threads. To handle this
+ * we create an extra thread for completing IO for GLOBAL_SCOPE threads.
+ * We don't really want to complete IO on a separate thread for LOCAL_SCOPE
+ * threads because it means extra context switches, which are really slow
+ * on NT... Since we're using a single completion port, some IO will
+ * be incorrectly completed on the GLOBAL_SCOPE IO thread; this will mean
+ * extra context switching; but I don't think there is anything I can do
+ * about it.
+ */
+
+#include "primpl.h"
+#include <direct.h>
+
+static HANDLE _pr_completion_port;
+static PRThread *_pr_io_completion_thread;
+
+#define RECYCLE_SIZE 512
+static struct _MDLock _pr_recycle_lock;
+static PRInt32 _pr_recycle_array[RECYCLE_SIZE];
+static PRInt32 _pr_recycle_tail = 0;
+
+__declspec(thread) PRThread *_pr_io_restarted_io = NULL;
+PRBool _nt_version_gets_lockfile_completion;
+
+struct _MDLock _pr_ioq_lock;
+extern _MDLock _nt_idleLock;
+extern PRCList _nt_idleList;
+extern PRUint32 _nt_idleCount;
+
+#define CLOSE_TIMEOUT PR_SecondsToInterval(5)
+
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ * This constant is used by _PR_FileTimeToPRTime().
+ */
+static const PRTime _pr_filetime_offset = 116444736000000000i64;
+
+#define _NEED_351_FILE_LOCKING_HACK
+#ifdef _NEED_351_FILE_LOCKING_HACK
+#define _PR_LOCAL_FILE 1
+#define _PR_REMOTE_FILE 2
+PRBool IsFileLocalInit();
+PRInt32 IsFileLocal(HANDLE hFile);
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+static PRInt32 _md_Associate(HANDLE);
+static PRInt32 _md_MakeNonblock(HANDLE);
+
+/* The _nt_use_async flag is used to prevent nspr from using any async io.
+ * this is a temporary hack. Don't learn to rely on it.
+ */
+static int _nt_use_async = 1;
+PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime);
+PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
+PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime);
+PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime);
+PRInt32 _nt_nonblock_sendto(PRFileDesc *, const char *, int, const struct sockaddr *, int, PRIntervalTime);
+PRInt32 _nt_nonblock_recvfrom(PRFileDesc *, char *, int, struct sockaddr *, int *, PRIntervalTime);
+
+/*
+ * UDP support
+ *
+ * UDP is supported on NT by the continuation thread mechanism.
+ * The code is borrowed from ptio.c in pthreads nspr, hence the
+ * PT and pt prefixes. This mechanism is in fact general and
+ * not limited to UDP. For now, only UDP's recvfrom and sendto
+ * go through the continuation thread if they get WSAEWOULDBLOCK
+ * on first try. Recv and send on a connected UDP socket still
+ * goes through asychronous io.
+ */
+
+#define PT_DEFAULT_SELECT_MSEC 100
+
+typedef struct pt_Continuation pt_Continuation;
+typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revent);
+
+typedef enum pr_ContuationStatus
+{
+ pt_continuation_sumbitted,
+ pt_continuation_inprogress,
+ pt_continuation_abort,
+ pt_continuation_done
+} pr_ContuationStatus;
+
+struct pt_Continuation
+{
+ /* These objects are linked in ascending timeout order */
+ pt_Continuation *next, *prev; /* self linked list of these things */
+
+ /* The building of the continuation operation */
+ ContinuationFn function; /* what function to continue */
+ union { SOCKET osfd; } arg1; /* #1 - the op's fd */
+ union { void* buffer; } arg2; /* #2 - primary transfer buffer */
+ union { PRIntn amount; } arg3; /* #3 - size of 'buffer' */
+ union { PRIntn flags; } arg4; /* #4 - read/write flags */
+ union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */
+
+ PRIntervalTime timeout; /* representation of the timeout */
+
+ PRIntn event; /* flags for select()'s events */
+
+ /*
+ ** The representation and notification of the results of the operation.
+ ** These function can either return an int return code or a pointer to
+ ** some object.
+ */
+ union { PRIntn code; void *object; } result;
+
+ PRIntn syserrno; /* in case it failed, why (errno) */
+ pr_ContuationStatus status; /* the status of the operation */
+ PRCondVar *complete; /* to notify the initiating thread */
+};
+
+static struct pt_TimedQueue
+{
+ PRLock *ml; /* a little protection */
+ PRThread *thread; /* internal thread's identification */
+ PRCondVar *new_op; /* new operation supplied */
+ PRCondVar *finish_op; /* an existing operation finished */
+ PRUintn op_count; /* number of operations in the list */
+ pt_Continuation *head, *tail; /* head/tail of list of operations */
+
+ pt_Continuation *op; /* timed operation furthest in future */
+ PRIntervalTime epoch; /* the epoch of 'timed' */
+} pt_tq;
+
+#if defined(DEBUG)
+static struct pt_debug_s
+{
+ PRIntn predictionsFoiled;
+ PRIntn pollingListMax;
+ PRIntn continuationsServed;
+} pt_debug;
+#endif /* DEBUG */
+
+static void ContinuationThread(void *arg);
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout);
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout);
+
+
+/* The key returned from GetQueuedCompletionStatus() is used to determine what
+ * type of completion we have. We differentiate between IO completions and
+ * CVAR completions.
+ */
+#define KEY_IO 0xaaaaaaaa
+#define KEY_CVAR 0xbbbbbbbb
+
+PRInt32
+_PR_MD_PAUSE_CPU(PRIntervalTime ticks)
+{
+ int awoken = 0;
+ unsigned long bytes, key;
+ int rv;
+ LPOVERLAPPED olp;
+ PRThread *completed_io;
+ PRUint32 timeout;
+
+ if (_nt_idleCount > 0) {
+ PRThread *deadThread;
+
+ _MD_LOCK(&_nt_idleLock);
+ while( !PR_CLIST_IS_EMPTY(&_nt_idleList) ) {
+ deadThread = _PR_THREAD_PTR(PR_LIST_HEAD(&_nt_idleList));
+ PR_REMOVE_LINK(&deadThread->links);
+
+ PR_ASSERT(deadThread->state == _PR_DEAD_STATE);
+
+ /* XXXMB - cleanup to do here? */
+ if ( !_PR_IS_NATIVE_THREAD(deadThread) ){
+ /* Spinlock while user thread is still running.
+ * There is no way to use a condition variable here. The thread
+ * is dead, and we have to wait until we switch off the dead
+ * thread before we can kill the fiber completely.
+ */
+ while ( deadThread->no_sched)
+ ;
+
+ DeleteFiber(deadThread->md.fiber_id);
+ }
+ memset(deadThread, 0xa, sizeof(PRThread)); /* debugging */
+ if (!deadThread->threadAllocatedOnStack)
+ PR_DELETE(deadThread);
+ _nt_idleCount--;
+ }
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+
+ if (ticks == PR_INTERVAL_NO_TIMEOUT)
+#if 0
+ timeout = INFINITE;
+#else
+ /* temporary hack to poll the runq every 5 seconds because of bug in
+ * native threads creating user threads and not poking the right cpu.
+ */
+ timeout = 5000;
+#endif
+ else
+ timeout = PR_IntervalToMilliseconds(ticks);
+
+ /*
+ * The idea of looping here is to complete as many IOs as possible before
+ * returning. This should minimize trips to the idle thread.
+ */
+ while(1) {
+ rv = GetQueuedCompletionStatus(
+ _pr_completion_port,
+ &bytes,
+ &key,
+ &olp,
+ timeout);
+ if (rv == 0 && olp == NULL) {
+ /* Error in GetQueuedCompetionStatus */
+ if (GetLastError() != WAIT_TIMEOUT) {
+ /* ARGH - what can we do here? Log an error? XXXMB */
+ return -1;
+ } else {
+ /* If awoken == 0, then we just had a timeout */
+ return awoken;
+ }
+ }
+
+ if (olp == NULL)
+ return 0;
+
+ completed_io = _PR_THREAD_MD_TO_PTR(olp);
+ completed_io->md.blocked_io_status = rv;
+ if (rv == 0)
+ completed_io->md.blocked_io_error = GetLastError();
+ completed_io->md.blocked_io_bytes = bytes;
+
+ if ( !_PR_IS_NATIVE_THREAD(completed_io) ) {
+ int pri = completed_io->priority;
+ _PRCPU *lockedCPU = _PR_MD_CURRENT_CPU();
+
+ /* The KEY_CVAR notification only occurs when a native thread
+ * is notifying a user thread. For user-user notifications
+ * the wakeup occurs by having the notifier place the thread
+ * on the runq directly; for native-native notifications the
+ * wakeup occurs by calling ReleaseSemaphore.
+ */
+ if ( key == KEY_CVAR ) {
+ PR_ASSERT(completed_io->io_pending == PR_FALSE || completed_io->io_suspended == PR_TRUE);
+
+ /* Thread has already been deleted from sleepQ */
+
+ /* Switch CPU and add to runQ */
+ completed_io->cpu = lockedCPU;
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ PR_ASSERT(key == KEY_IO);
+ PR_ASSERT(completed_io->io_pending == PR_TRUE);
+
+ _PR_THREAD_LOCK(completed_io);
+
+ completed_io->io_pending = PR_FALSE;
+
+ /* If io_suspended is true, then this IO has already resumed.
+ * We don't need to do anything; because the thread is
+ * already running.
+ */
+ if (completed_io->io_suspended == PR_FALSE) {
+ if (completed_io->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) {
+ _PR_SLEEPQ_LOCK(completed_io->cpu);
+ _PR_DEL_SLEEPQ(completed_io, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(completed_io->cpu);
+
+ _PR_THREAD_UNLOCK(completed_io);
+
+ completed_io->cpu = lockedCPU;
+ completed_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(lockedCPU);
+ _PR_ADD_RUNQ(completed_io, lockedCPU, pri);
+ _PR_RUNQ_UNLOCK(lockedCPU);
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ } else {
+ _PR_THREAD_UNLOCK(completed_io);
+ }
+ }
+ } else {
+ int old_count;
+ PRBool fNeedRelease = PR_FALSE;
+
+ /* For native threads, they are only notified through this loop
+ * when completing IO. So, don't worry about this being a CVAR
+ * notification, because that is not possible.
+ */
+ _PR_THREAD_LOCK(completed_io);
+ completed_io->io_pending = PR_FALSE;
+ if (completed_io->io_suspended == PR_FALSE) {
+ completed_io->state = _PR_RUNNABLE;
+ fNeedRelease = PR_TRUE;
+ }
+ _PR_THREAD_UNLOCK(completed_io);
+ if (fNeedRelease) {
+ rv = ReleaseSemaphore(completed_io->md.blocked_sema,
+ 1, &old_count);
+ PR_ASSERT(0 != rv);
+ }
+ }
+
+ awoken++;
+ timeout = 0; /* Don't block on subsequent trips through the loop */
+ }
+
+ /* never reached */
+ return 0;
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ if ( thread->flags & _PR_GLOBAL_SCOPE ) {
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv) {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ if (thread->io_pending == PR_TRUE) {
+ thread->io_suspended = PR_TRUE;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The IO completed just at the same time the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ thread->state = _PR_RUNNING;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+ } else {
+ PRInt32 is;
+
+ /* XXXMB - This is barely safe, but works. We should find a
+ * way to make all callers of PR_MD_WAIT zero the overlapped buffer
+ * themselves...
+ */
+ if (thread->state != _PR_IO_WAIT)
+ memset(&(thread->md.overlapped), 0, sizeof(OVERLAPPED));
+ if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_INTSOFF(is);
+ _PR_MD_SWITCH_CONTEXT(thread);
+ }
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+_NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+
+ _PR_THREAD_LOCK(thread);
+
+ /* The IO may have already completed; if so, don't add to sleepQ,
+ * since we are already on the runQ!
+ */
+ if (thread->io_pending == PR_TRUE) {
+ _PR_SLEEPQ_LOCK(thread->cpu);
+ _PR_ADD_SLEEPQ(thread, timeout);
+ _PR_SLEEPQ_UNLOCK(thread->cpu);
+ } else
+ fWait = PR_FALSE;
+ _PR_THREAD_UNLOCK(thread);
+ }
+ if (fWait)
+ return _PR_MD_WAIT(thread, timeout);
+ else
+ return PR_SUCCESS;
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ PRStatus rv;
+ _PRCPU *cpu = thr->cpu;
+
+ PR_ASSERT(thr->state == _PR_IO_WAIT);
+ thr->io_suspended = PR_TRUE;
+ thr->state = _PR_RUNNABLE;
+
+ if (!_PR_IS_NATIVE_THREAD(thr)) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ _PR_AddThreadToRunQ(me, thr);
+ }
+ _PR_THREAD_UNLOCK(thr);
+ rv = _PR_MD_WAKEUP_WAITER(thr);
+ PR_ASSERT(PR_SUCCESS == rv);
+}
+
+/* Resume an outstanding IO; requires that after the switch, we disable */
+static PRStatus
+_NT_ResumeIO(PRThread *thread, PRIntervalTime ticks)
+{
+ PRBool fWait = PR_TRUE;
+
+ if (!_PR_IS_NATIVE_THREAD(thread)) {
+ _pr_io_restarted_io = thread;
+ } else {
+ _PR_THREAD_LOCK(thread);
+ if (!thread->io_pending)
+ fWait = PR_FALSE;
+ thread->io_suspended = PR_FALSE;
+
+ _PR_THREAD_UNLOCK(thread);
+ }
+ /* We don't put ourselves back on the sleepQ yet; until we
+ * set the suspended bit to false, we can't do that. Just save
+ * the sleep time here, and then continue. The restarted_io handler
+ * will add us to the sleepQ if needed.
+ */
+ thread->sleep = ticks;
+
+ if (fWait)
+ return _PR_MD_WAIT(thread, ticks);
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if (thread == NULL) {
+ /* If thread is NULL, we aren't waking a thread, we're just poking
+ * idle thread
+ */
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, NULL) == FALSE)
+ return PR_FAILURE;
+ return PR_SUCCESS;
+ }
+
+ if ( _PR_IS_NATIVE_THREAD(thread) ) {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+ } else {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ /* When a Native thread has to awaken a user thread, it has to poke
+ * the completion port because all user threads might be idle, and
+ * thus the CPUs are just waiting for a completion.
+ *
+ * XXXMB - can we know when we are truely idle (and not checking
+ * the runq)?
+ */
+ if (_PR_IS_NATIVE_THREAD(me) || (thread->cpu != me->cpu)) {
+ /* The thread should not be in any queue */
+ PR_ASSERT(thread->queueCount == 0);
+ if ( PostQueuedCompletionStatus(_pr_completion_port, 0,
+ KEY_CVAR, &(thread->md.overlapped)) == FALSE)
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+ }
+}
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+ OSVERSIONINFO OSversion;
+
+ WSAStartup( WSAVersion, &WSAData );
+
+ _pr_completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
+ NULL,
+ 0,
+ 0);
+
+ _MD_NEW_LOCK(&_pr_recycle_lock);
+ _MD_NEW_LOCK(&_pr_ioq_lock);
+
+ OSversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&OSversion)) {
+ _nt_version_gets_lockfile_completion = PR_FALSE;
+ if (OSversion.dwMajorVersion >= 4) {
+ _nt_version_gets_lockfile_completion = PR_TRUE;
+ }
+ } else
+ PR_ASSERT(0);
+
+ IsFileLocalInit();
+
+ /*
+ * UDP support: start up the continuation thread
+ */
+
+ pt_tq.op_count = 0;
+ pt_tq.head = pt_tq.tail = NULL;
+ pt_tq.ml = PR_NewLock();
+ PR_ASSERT(NULL != pt_tq.ml);
+ pt_tq.new_op = PR_NewCondVar(pt_tq.ml);
+ PR_ASSERT(NULL != pt_tq.new_op);
+#if defined(DEBUG)
+ memset(&pt_debug, 0, sizeof(struct pt_debug_s));
+#endif
+
+ pt_tq.thread = PR_CreateThread(
+ PR_SYSTEM_THREAD, ContinuationThread, NULL,
+ PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
+
+ PR_ASSERT(NULL != pt_tq.thread);
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+}
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+/* _md_get_recycled_socket()
+ * Get a socket from the recycle bin; if no sockets are in the bin,
+ * create one. The socket will be passed to AcceptEx() as the
+ * second argument.
+ */
+static SOCKET
+_md_get_recycled_socket()
+{
+ SOCKET rv;
+ int af = AF_INET;
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (_pr_recycle_tail) {
+ _pr_recycle_tail--;
+ rv = _pr_recycle_array[_pr_recycle_tail];
+ _MD_UNLOCK(&_pr_recycle_lock);
+ return rv;
+ }
+ _MD_UNLOCK(&_pr_recycle_lock);
+
+#ifdef _PR_INET6
+ if (_pr_ipv6_enabled) {
+ af = AF_INET6;
+ }
+#endif
+ rv = _PR_MD_SOCKET(af, SOCK_STREAM, 0);
+ if (rv != INVALID_SOCKET && _md_Associate((HANDLE)rv) == 0) {
+ closesocket(rv);
+ return INVALID_SOCKET;
+ }
+ return rv;
+}
+
+/* _md_put_recycled_socket()
+ * Add a socket to the recycle bin.
+ */
+static void
+_md_put_recycled_socket(SOCKET newsock)
+{
+ PR_ASSERT(_pr_recycle_tail >= 0);
+
+ _MD_LOCK(&_pr_recycle_lock);
+ if (_pr_recycle_tail < RECYCLE_SIZE) {
+ _pr_recycle_array[_pr_recycle_tail] = newsock;
+ _pr_recycle_tail++;
+ _MD_UNLOCK(&_pr_recycle_lock);
+ } else {
+ _MD_UNLOCK(&_pr_recycle_lock);
+ closesocket(newsock);
+ }
+
+ return;
+}
+
+/* _md_Associate()
+ * Associates a file with the completion port.
+ * Returns 0 on failure, 1 on success.
+ */
+static PRInt32
+_md_Associate(HANDLE file)
+{
+ HANDLE port;
+
+ port = CreateIoCompletionPort((HANDLE)file,
+ _pr_completion_port,
+ KEY_IO,
+ 0);
+
+ /* XXX should map error codes on failures */
+ return (port == _pr_completion_port);
+}
+
+/*
+ * _md_MakeNonblock()
+ * Make a socket nonblocking.
+ * Returns 0 on failure, 1 on success.
+ */
+static PRInt32
+_md_MakeNonblock(HANDLE file)
+{
+ int rv;
+ u_long one = 1;
+
+ rv = ioctlsocket((SOCKET)file, FIONBIO, &one);
+ /* XXX should map error codes on failures */
+ return (rv == 0);
+}
+
+static int missing_completions = 0;
+static int max_wait_loops = 0;
+
+static PRInt32
+_NT_IO_ABORT(PRInt32 sock)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRBool fWait;
+ PRInt32 rv;
+ int loop_count;
+
+ /* This is a clumsy way to abort the IO, but it is all we can do.
+ * It looks a bit racy, but we handle all the cases.
+ * case 1: IO completes before calling closesocket
+ * case 1a: fWait is set to PR_FALSE
+ * This should e the most likely case. We'll properly
+ * not wait call _NT_IO_WAIT, since the closesocket()
+ * won't be forcing a completion.
+ * case 1b: fWait is set to PR_TRUE
+ * This hopefully won't happen much. When it does, this
+ * thread will timeout in _NT_IO_WAIT for CLOSE_INTERVAL
+ * before cleaning up.
+ * case 2: IO does not complete before calling closesocket
+ * case 2a: IO never completes
+ * This is the likely case. We'll close it and wait
+ * for the completion forced by the close. Return should
+ * be immediate.
+ * case 2b: IO completes just after calling closesocket
+ * Since the closesocket is issued, we'll either get a
+ * completion back for the real IO or for the close. We
+ * don't really care. It may not even be possible to get
+ * a real completion here. In any event, we'll awaken
+ * from NT_IO_WAIT immediately.
+ */
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ /*
+ * If there's still I/O pending, it should have already timed
+ * out once before this function is called.
+ */
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+
+ /* Set up to wait for I/O completion again */
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ /* Close the socket if there is one */
+ if (sock != INVALID_SOCKET) {
+ rv = closesocket((SOCKET)sock);
+ }
+
+ /* If there was I/O pending before the close, wait for it to complete */
+ if (fWait) {
+
+ /* Wait and wait for the I/O to complete */
+ for (loop_count = 0; fWait; ++loop_count) {
+
+ _NT_IO_WAIT(me, CLOSE_TIMEOUT);
+
+ _PR_THREAD_LOCK(me);
+ fWait = me->io_pending;
+ if (fWait) {
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+ me->state = _PR_IO_WAIT;
+ me->io_suspended = PR_FALSE;
+ }
+ _PR_THREAD_UNLOCK(me);
+
+ if (loop_count > max_wait_loops) {
+ max_wait_loops = loop_count;
+ }
+ }
+
+ if (loop_count > 1) {
+ ++missing_completions;
+ }
+
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ me->io_suspended = PR_FALSE;
+
+ return rv;
+}
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET) {
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+ }
+
+ return (PRInt32)sock;
+}
+
+struct connect_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ PRInt32 osfd;
+ struct sockaddr *addr;
+ PRUint32 addrlen;
+ PRIntervalTime timeout;
+};
+
+void
+_PR_MD_connect_thread(void *cdata)
+{
+ struct connect_data_s *cd = (struct connect_data_s *)cdata;
+
+ cd->status = connect(cd->osfd, cd->addr, cd->addrlen);
+
+ if (cd->status == SOCKET_ERROR)
+ cd->error = WSAGetLastError();
+
+ return;
+}
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv;
+ PRThread *cThread;
+ struct connect_data_s cd;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ PRInt32 rv;
+ fd_set wd;
+ struct timeval tv, *tvp;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ while ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) {
+ rv = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && ((rv == WSAEWOULDBLOCK) ||
+ (rv == WSAEALREADY) ||
+ (rv == WSAEINVAL) /* for winsock1.1, it reports EALREADY as EINVAL */)) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ rv = select(osfd + 1, NULL, &wd, NULL, tvp);
+ if (rv > 0) {
+ rv = 0;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return(-1);
+ } else if (rv < 0) {
+ rv = WSAGetLastError();
+ _PR_MD_MAP_SELECT_ERROR(rv);
+ return(-1);
+ }
+ } else if ((rv == WSAEISCONN)) {
+ /* Success! */
+ return 0;
+ } else {
+ _PR_MD_MAP_CONNECT_ERROR(rv);
+ return -1;
+ }
+ }
+ return rv;
+ }
+
+ /* If we are a native thread, just make the blocking IO call */
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ rv = connect(osfd, (struct sockaddr *)addr, addrlen);
+ if (rv == -1) {
+ rv = WSAGetLastError();
+ _PR_MD_MAP_CONNECT_ERROR(rv);
+ return -1;
+ } else
+ return rv;
+ }
+
+ /* NT doesn't provide a nice way to do asynchronous
+ * connect. The proxy team invented a huge chunk of code which has
+ * a single thread multiplexing multiple connect requests via
+ * WSAAsyncSelect(). That is a better solution, but I'm not doing that
+ * now. At this point, just create a real thread to do the work.
+ *
+ * Rumor has it that on nt3.51, all the WSA library does is create
+ * a thread to call a blocking connect() anyway. On 4.0 they've fixed
+ * that. -mbelshe
+ */
+ cd.osfd = osfd;
+ cd.addr = (struct sockaddr *)addr;
+ cd.addrlen = addrlen;
+ cd.timeout = timeout;
+ cThread = PR_CreateThread(PR_SYSTEM_THREAD,
+ _PR_MD_connect_thread,
+ (void *)&cd,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+
+ if (cThread == NULL) {
+ return -1;
+ }
+
+ PR_JoinThread(cThread);
+
+ rv = cd.status;
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_CONNECT_ERROR(cd.error);
+ return -1;
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+#if 0
+ int one = 1;
+#endif
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+#if 0
+ /* Disable nagle- so far unknown if this is good or not...
+ */
+ rv = setsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ TCP_NODELAY,
+ (const char *)&one,
+ sizeof(one));
+ PR_ASSERT(rv == 0);
+#endif
+
+ return 0;
+}
+
+void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 accept_sock, PRInt32 listen_sock)
+{
+ /* Sockets accept()'d with AcceptEx need to call this setsockopt before
+ * calling anything other than ReadFile(), WriteFile(), send(), recv(),
+ * Transmitfile(), and closesocket(). In order to call any other
+ * winsock functions, we have to make this setsockopt call.
+ *
+ * XXXMB - For the server, we *NEVER* need this in
+ * the "normal" code path. But now we have to call it. This is a waste
+ * of a system call. We'd like to only call it before calling the
+ * obscure socket calls, but since we don't know at that point what the
+ * original socket was (or even if it is still alive) we can't do it
+ * at that point...
+ */
+ setsockopt((SOCKET)accept_sock,
+ SOL_SOCKET,
+ SO_UPDATE_ACCEPT_CONTEXT,
+ (char *)&listen_sock,
+ sizeof(listen_sock));
+
+}
+
+#define INET_ADDR_PADDED (sizeof(PRNetAddr) + 16)
+PRInt32
+_PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout, PRBool fast,
+ _PR_AcceptTimeoutCallback callback, void *callbackArg)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ SOCKET accept_sock;
+ int bytes;
+ PRNetAddr *Laddr;
+ PRNetAddr *Raddr;
+ PRUint32 llen, err;
+ int rv;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ /*
+ * The accepted socket inherits the nonblocking attribute of
+ * the listening socket, so no need to call _md_MakeNonblock().
+ */
+ return _nt_nonblock_accept(fd, (struct sockaddr_in *)raddr, rlen, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ if (!me->md.acceptex_buf) {
+ me->md.acceptex_buf = PR_MALLOC(2*INET_ADDR_PADDED);
+ if (!me->md.acceptex_buf) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+
+ accept_sock = _md_get_recycled_socket();
+ if (accept_sock == INVALID_SOCKET)
+ return -1;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = AcceptEx((SOCKET)osfd,
+ accept_sock,
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped));
+
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ /* Argh! The IO failed */
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (!fast)
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)accept_sock, (SOCKET)osfd);
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ me->md.acceptex_buf,
+ 0,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)&(Raddr),
+ (unsigned int *)rlen);
+
+ if (raddr != NULL)
+ memcpy((char *)raddr, (char *)&Raddr->inet, *rlen);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return accept_sock;
+}
+
+PRInt32
+_PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr,
+ void *buf, PRInt32 amount, PRIntervalTime timeout,
+ PRBool update, _PR_AcceptTimeoutCallback callback,
+ void *callbackArg)
+{
+ PRInt32 sock = sd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ PRNetAddr *Laddr;
+ PRUint32 llen, rlen, err;
+ int rv;
+ PRBool isConnected;
+ PRBool madeCallback = PR_FALSE;
+
+ if (!_nt_use_async) {
+ PRFileDesc *nd;
+ bytes = _PR_EmulateAcceptRead(sd, &nd, raddr, buf, amount, timeout);
+ if (bytes != -1) {
+ /*
+ * This part is the same as SocketClose(nd), except
+ * that we don't close the osfd.
+ */
+ PR_ASSERT(nd->secret->state == _PR_FILEDESC_OPEN);
+ *newSock = nd->secret->md.osfd;
+ nd->secret->state = _PR_FILEDESC_CLOSED;
+ PR_FreeFileDesc(nd);
+ }
+ return bytes;
+ }
+
+ if (!sd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock);
+ PR_ASSERT(0 != rv);
+ sd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ *newSock = _md_get_recycled_socket();
+ if (*newSock == INVALID_SOCKET)
+ return -1;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = sock;
+ me->state = _PR_IO_WAIT;
+ rv = AcceptEx((SOCKET)sock,
+ *newSock,
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ &bytes,
+ &(me->md.overlapped));
+
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_ACCEPTEX_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+retry:
+ if (me->io_suspended) {
+ PRInt32 err;
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ PR_ASSERT(timeout != PR_INTERVAL_NO_TIMEOUT);
+
+ err = getsockopt(*newSock,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *)&seconds,
+ (PINT)&bytes);
+ if ( err == NO_ERROR ) {
+ PRIntervalTime elapsed = PR_SecondsToInterval(seconds);
+
+ if (seconds == 0xffffffff)
+ isConnected = PR_FALSE;
+ else
+ isConnected = PR_TRUE;
+
+ if (!isConnected) {
+ if (madeCallback == PR_FALSE && callback)
+ callback(callbackArg);
+ madeCallback = PR_TRUE;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
+ return -1;
+ goto retry;
+ }
+
+ if (elapsed < timeout) {
+ /* Socket is not connected but time not elapsed, RESUME IO */
+ timeout -= elapsed;
+ me->state = _PR_IO_WAIT;
+ if (_NT_ResumeIO(me, timeout) == PR_FAILURE)
+ return -1;
+ goto retry;
+ }
+ } else {
+ /* What to do here? Assume socket not open?*/
+ PR_ASSERT(0);
+ isConnected = PR_FALSE;
+ }
+
+ rv = _NT_IO_ABORT(*newSock);
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ /* If the IO is still suspended, it means we didn't get any
+ * completion from NT_IO_WAIT. This is not disasterous, I hope,
+ * but it may mean we still have an IO outstanding... Try to
+ * recover by just allowing ourselves to continue.
+ */
+ me->io_suspended = PR_FALSE;
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ me->state = _PR_RUNNING;
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_ACCEPTEX_ERROR(me->md.blocked_io_error);
+ closesocket(*newSock);
+ return -1;
+ }
+
+ if (update)
+ _PR_MD_UPDATE_ACCEPT_CONTEXT((SOCKET)*newSock, (SOCKET)sock);
+
+ /* IO is done */
+ GetAcceptExSockaddrs(
+ buf,
+ amount,
+ INET_ADDR_PADDED,
+ INET_ADDR_PADDED,
+ (LPSOCKADDR *)&(Laddr),
+ &llen,
+ (LPSOCKADDR *)(raddr),
+ (unsigned int *)&rlen);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen,
+ PRInt32 flags, PRIntervalTime timeout)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 tflags;
+ int rv, err;
+
+ if (!_nt_use_async) {
+ if (!sock->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)sock->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ sock->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _PR_EmulateTransmitFile(sock, file, headers, hlen, flags, timeout);
+ }
+
+ if (!sock->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)sock->secret->md.osfd);
+ PR_ASSERT(0 != rv);
+ sock->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (!me->md.xmit_bufs) {
+ me->md.xmit_bufs = PR_NEW(TRANSMIT_FILE_BUFFERS);
+ if (!me->md.xmit_bufs) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ }
+ me->md.xmit_bufs->Head = (void *)headers;
+ me->md.xmit_bufs->HeadLength = hlen;
+ me->md.xmit_bufs->Tail = (void *)NULL;
+ me->md.xmit_bufs->TailLength = 0;
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ tflags = 0;
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET)
+ tflags = TF_DISCONNECT | TF_REUSE_SOCKET;
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = sock->secret->md.osfd;
+ me->state = _PR_IO_WAIT;
+ rv = TransmitFile((SOCKET)sock->secret->md.osfd,
+ (HANDLE)file->secret->md.osfd,
+ (DWORD)0,
+ (DWORD)0,
+ (LPOVERLAPPED)&(me->md.overlapped),
+ (TRANSMIT_FILE_BUFFERS *)me->md.xmit_bufs,
+ (DWORD)tflags);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_TRANSMITFILE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_TRANSMITFILE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) {
+ _md_put_recycled_socket(sock->secret->md.osfd);
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_recv(fd, buf, amount, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = ReadFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if ((err = GetLastError()) == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ int bytes;
+ int rv, err;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_send(fd, (char *)buf, amount, timeout);
+ }
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_Associate((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = osfd;
+ me->state = _PR_IO_WAIT;
+ rv = WriteFile((HANDLE)osfd,
+ buf,
+ amount,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_nt_use_async && !fd->secret->nonblocking)
+ return pt_SendTo(osfd, buf, amount, flags, addr, addrlen, timeout);
+ else
+ return _nt_nonblock_sendto(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ if (_nt_use_async && !fd->secret->nonblocking)
+ return pt_RecvFrom(osfd, buf, amount, flags, addr, addrlen, timeout);
+ else
+ return _nt_nonblock_recvfrom(fd, buf, amount, (struct sockaddr *)addr, addrlen, timeout);
+}
+
+/* XXXMB - for now this is a sockets call only */
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ int index;
+ int sent = 0;
+ int rv;
+
+ if (!_nt_use_async || fd->secret->nonblocking) {
+ if (!fd->secret->md.io_model_committed) {
+ rv = _md_MakeNonblock((HANDLE)osfd);
+ PR_ASSERT(0 != rv);
+ fd->secret->md.io_model_committed = PR_TRUE;
+ }
+ return _nt_nonblock_writev(fd, iov, iov_size, timeout);
+ }
+
+ for (index=0; index<iov_size; index++) {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0,
+ timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len ) {
+ if (sent <= 0)
+ return -1;
+ return -1;
+ }
+ }
+
+ return sent;
+}
+
+PRInt32
+_PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ PRInt32 rv;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if (rv < 0)
+ _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+ PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return(rv);
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ /*
+ * NT has a bug that, when invoked on a socket accepted by
+ * AcceptEx(), getpeername() returns an all-zero peer address.
+ * To work around this bug, we store the peer's address (returned
+ * by AcceptEx()) with the socket fd and use the cached peer
+ * address if the socket is an accepted socket.
+ */
+
+ if (fd->secret->md.accepted_socket) {
+ INT seconds;
+ INT bytes = sizeof(seconds);
+
+ /*
+ * Determine if the socket is connected.
+ */
+
+ rv = getsockopt(fd->secret->md.osfd,
+ SOL_SOCKET,
+ SO_CONNECT_TIME,
+ (char *) &seconds,
+ (PINT) &bytes);
+ if (rv == NO_ERROR) {
+ if (seconds == 0xffffffff) {
+ PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+ *len = PR_NETADDR_SIZE(addr);
+ memcpy(addr, &fd->secret->md.peer_addr, *len);
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ } else {
+ rv = getpeername((SOCKET)fd->secret->md.osfd,
+ (struct sockaddr *) addr, len);
+ if (rv == 0) {
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+/* --- FILE IO ----------------------------------------------------------- */
+
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
+
+ if (_nt_use_async)
+ {
+ if (osflags & PR_RDONLY || osflags & PR_RDWR) access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR) access |= GENERIC_WRITE;
+
+ if (osflags & PR_CREATE_FILE)
+ flags = (0 != (osflags & PR_TRUNCATE)) ? CREATE_ALWAYS : OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE) flags = CREATE_ALWAYS;
+ else flags = OPEN_EXISTING;
+
+ flag6 |= FILE_FLAG_OVERLAPPED;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (_md_Associate(file) == 0) {
+ CloseHandle(file);
+ return -1;
+ }
+
+ if (osflags & PR_APPEND) {
+ if ( SetFilePointer(file, 0, 0, FILE_END) == 0xFFFFFFFF ) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ CloseHandle(file);
+ return -1;
+ }
+ }
+
+ return (PRInt32)file;
+ }
+ else
+ {
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR)
+ access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR)
+ access |= GENERIC_WRITE;
+ if (osflags & PR_CREATE_FILE)
+ flags = OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE)
+ flags = CREATE_ALWAYS;
+ else
+ flags = OPEN_EXISTING;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ /* Note: we didn't bother putting it in nonblocking mode */
+ return (PRInt32)file;
+ }
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRUint32 bytes;
+ int rv, err;
+
+ if (_nt_use_async && !fd->secret->md.nonoverlapped) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = f;
+ me->state = _PR_IO_WAIT;
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ &me->md.overlapped);
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ if (err == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ if (me->md.blocked_io_error == ERROR_HANDLE_EOF)
+ return 0;
+ _PR_MD_MAP_READ_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ } else {
+
+ rv = ReadFile((HANDLE)f,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE) {
+ /* The write end of the pipe has been closed. */
+ return 0;
+ }
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (_nt_use_async && !fd->secret->md.nonoverlapped) {
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->md.overlapped.Offset = SetFilePointer((HANDLE)f, 0, 0, FILE_CURRENT);
+
+ me->io_pending = PR_TRUE;
+ me->io_fd = f;
+ me->state = _PR_IO_WAIT;
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ &(me->md.overlapped));
+ if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_WRITE_ERROR(err);
+ return -1;
+ }
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ PR_ASSERT(0);
+ return -1;
+ }
+
+ PR_ASSERT(me->io_pending == PR_FALSE || me->io_suspended == PR_TRUE);
+
+ if (me->io_suspended) {
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ } else {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ return -1;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_WRITE_ERROR(me->md.blocked_io_error);
+ return -1;
+ }
+
+ SetFilePointer((HANDLE)f, me->md.blocked_io_bytes, 0, FILE_CURRENT);
+
+ PR_ASSERT(me->io_pending == PR_FALSE);
+
+ return me->md.blocked_io_bytes;
+ } else {
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL);
+ if (rv == 0) {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+ }
+}
+
+PRInt32
+_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, NULL, whence);
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ }
+ return rv;
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
+{
+ PRInt64 result;
+ PRInt32 rv, low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv)
+ {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ }
+
+ result = (hi << 32) + rv;
+ return result;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was
+ * because of this, and if it was, I ignore the error.
+ */
+
+ long handle = _get_osfhandle(fd->secret->md.osfd);
+ BOOL ok = FlushFileBuffers((HANDLE)handle);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+
+ if (err != ERROR_ACCESS_DENIED) { /* from winerror.h */
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_PR_MD_CLOSE(PRInt32 osfd, PRBool socket)
+{
+ PRInt32 rv;
+ PRInt32 err;
+ if (_nt_use_async) {
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ if (socket) {
+ rv = closesocket((SOCKET)osfd);
+ if (rv < 0)
+ err = WSAGetLastError();
+ } else {
+ rv = CloseHandle((HANDLE)osfd)?0:-1;
+ if (rv < 0)
+ err = GetLastError();
+ }
+
+ if (rv == 0 && me->io_pending) {
+ if (me->io_fd == osfd) {
+ PRBool fWait;
+
+ PR_ASSERT(me->io_suspended == PR_TRUE);
+ _PR_THREAD_LOCK(me);
+ me->state = _PR_IO_WAIT;
+ /* The IO could have completed on another thread just after
+ * calling closesocket while the io_suspended flag was true.
+ * So we now grab the lock to do a safe check on io_pending to
+ * see if we need to wait or not. At this point we can check
+ * io_pending safely because we've reset io_suspended to FALSE.
+ * XXXMB - 1-15-97 this seems fishy and begging for a race...
+ */
+ fWait = me->io_pending;
+ me->io_suspended = PR_FALSE;
+ _PR_THREAD_UNLOCK(me);
+
+ if (fWait)
+ _NT_IO_WAIT(me, CLOSE_TIMEOUT);
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+ PR_ASSERT(me->io_pending == PR_FALSE);
+ me->io_suspended = PR_FALSE;
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ }
+ } else {
+ me->io_suspended = PR_FALSE;
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_ERROR(err);
+ }
+ return rv;
+ } else {
+ if (socket) {
+ rv = closesocket((SOCKET)osfd);
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+ } else {
+ rv = CloseHandle((HANDLE)osfd)?0:-1;
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ }
+ }
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRStatus
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose( d->d_hdl )) {
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+
+ PR_snprintf(filename, MAX_PATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFile(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
+ continue;
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFile(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+static void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATA findFileData;
+ char pathbuf[MAX_PATH + 1];
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * FindFirstFile() expands wildcard characters. So
+ * we make sure the pathname contains no wildcard.
+ */
+ if (NULL != strpbrk(fn, "?*")) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+ return -1;
+ }
+
+ hFindFile = FindFirstFile(fn, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ DWORD len;
+ char *filePart;
+
+ /*
+ * FindFirstFile() does not work correctly on root directories.
+ * It also doesn't work correctly on a pathname that ends in a
+ * slash. So we first check to see if the pathname specifies a
+ * root directory. If not, and if the pathname ends in a slash,
+ * we remove the final slash and try again.
+ */
+
+ /*
+ * If the pathname does not contain ., \, and /, it cannot be
+ * a root directory or a pathname that ends in a slash.
+ */
+ if (NULL == strpbrk(fn, ".\\/")) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
+ &filePart);
+ PR_ASSERT(0 != len);
+ if (len > sizeof(pathbuf)) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return -1;
+ }
+ if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
+ info->type = PR_FILE_DIRECTORY;
+ info->size = 0;
+ /*
+ * These timestamps don't make sense for root directories.
+ */
+ info->modifyTime = 0;
+ info->creationTime = 0;
+ return 0;
+ }
+ if (!_PR_IS_SLASH(pathbuf[len - 1])) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ } else {
+ pathbuf[len - 1] = '\0';
+ hFindFile = FindFirstFile(pathbuf, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ }
+
+ FindClose(hFindFile);
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFile(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+ PRInt32 rv;
+
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ rv = _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectory(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectory(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+
+ /* HACK AROUND NT BUG
+ * NT 3.51 has a bug. In NT 3.51, if LockFileEx returns true, you
+ * don't get any completion on the completion port. This is a bug.
+ *
+ * They fixed it on NT4.0 so that you do get a completion.
+ *
+ * If we pretend we won't get a completion, NSPR gets confused later
+ * when the unexpected completion arrives. If we assume we do get
+ * a completion, we hang on 3.51. Worse, Microsoft informs me that the
+ * behavior varies on 3.51 depending on if you are using a network
+ * file system or a local disk!
+ *
+ * Solution: For now, _nt_version_gets_lockfile_completion is set
+ * depending on whether or not this system is EITHER
+ * - running NT 4.0
+ * - running NT 3.51 with a service pack greater than 5.
+ *
+ * In the meantime, this code may not work on network file systems.
+ *
+ */
+
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ rv = LockFileEx((HANDLE)f,
+ LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+#ifdef _NEED_351_FILE_LOCKING_HACK
+ else if (rv) {
+ /* If this is NT 3.51 and the file is local, then we won't get a
+ * completion back from LockFile when it succeeded.
+ */
+ if (_nt_version_gets_lockfile_completion == PR_FALSE) {
+ if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_SUCCESS;
+ }
+ }
+ }
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) {
+ me->io_pending = PR_FALSE;
+ me->state = _PR_RUNNING;
+ return PR_FAILURE;
+ }
+
+ if (me->md.blocked_io_status == 0) {
+ _PR_MD_MAP_LOCKF_ERROR(me->md.blocked_io_error);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ memset(&(me->md.overlapped), 0, sizeof(OVERLAPPED));
+
+ PR_ASSERT(me->io_suspended == PR_FALSE);
+
+ rv = UnlockFileEx((HANDLE)f,
+ 0,
+ 0x7fffffff,
+ 0,
+ &me->md.overlapped);
+
+ if (rv)
+ return PR_SUCCESS;
+ else {
+ int err = GetLastError();
+ _PR_MD_MAP_LOCKF_ERROR(err);
+ return PR_FAILURE;
+ }
+}
+
+void
+_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
+{
+ /*
+ * On NT, we either call _md_Associate() or _md_MakeNonblock(),
+ * depending on whether the socket is blocking or not.
+ *
+ * Once we associate a socket with the io completion port,
+ * there is no way to disassociate it from the io completion
+ * port. So we have to call _md_Associate/_md_MakeNonblock
+ * lazily.
+ */
+}
+
+#ifdef _NEED_351_FILE_LOCKING_HACK
+/***************
+**
+** Lockfile hacks
+**
+** The following code is a hack to work around a microsoft bug with lockfile.
+** The problem is that on NT 3.51, if LockFileEx() succeeds, you never
+** get a completion back for files that are on local disks. So, we need to
+** know if a file is local or remote so we can tell if we should expect
+** a completion.
+**
+** The only way to check if a file is local or remote based on the handle is
+** to get the serial number for the volume it is mounted on and then to
+** compare that with mounted drives. This code caches the volume numbers of
+** fixed disks and does a relatively quick check.
+**
+** Locking: Since the only thing we ever do when multithreaded is a 32bit
+** assignment, we probably don't need locking. It is included just
+** case anyway.
+**
+** Limitations: Does not work on floppies because they are too slow
+** Unknown if it will work on wierdo 3rd party file systems
+**
+****************
+*/
+
+/* There can only be 26 drive letters on NT */
+#define _PR_MAX_DRIVES 26
+
+_MDLock cachedVolumeLock;
+DWORD dwCachedVolumeSerialNumbers[_PR_MAX_DRIVES] = {0};
+DWORD dwLastCachedDrive = 0;
+DWORD dwRemoveableDrivesToCheck = 0; /* bitmask for removeable drives */
+
+PRBool IsFileLocalInit()
+{
+ TCHAR lpBuffer[_PR_MAX_DRIVES*5];
+ DWORD nBufferLength = _PR_MAX_DRIVES*5;
+ DWORD nBufferNeeded = GetLogicalDriveStrings(0, NULL);
+ DWORD dwIndex = 0;
+ DWORD dwDriveType;
+ DWORD dwVolumeSerialNumber;
+ DWORD dwDriveIndex = 0;
+ DWORD oldmode = (DWORD) -1;
+
+ _MD_NEW_LOCK(&cachedVolumeLock);
+
+ nBufferNeeded = GetLogicalDriveStrings(nBufferLength, lpBuffer);
+ if (nBufferNeeded == 0 || nBufferNeeded > nBufferLength)
+ return PR_FALSE;
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // damn dialog.
+
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = 0;
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ // now loop through the logical drives
+ while(lpBuffer[dwIndex] != TEXT('\0'))
+ {
+ // skip the floppy drives. This is *SLOW*
+ if ((lpBuffer[dwIndex] == TEXT('A')) || (lpBuffer[dwIndex] == TEXT('B')))
+ /* Skip over floppies */;
+ else
+ {
+ dwDriveIndex = (lpBuffer[dwIndex] - TEXT('A'));
+
+ dwDriveType = GetDriveType(&lpBuffer[dwIndex]);
+
+ switch(dwDriveType)
+ {
+ // Ignore these drive types
+ case 0:
+ case 1:
+ case DRIVE_REMOTE:
+ default: // If the drive type is unknown, ignore it.
+ break;
+
+ // Removable media drives can have different serial numbers
+ // at different times, so cache the current serial number
+ // but keep track of them so they can be rechecked if necessary.
+ case DRIVE_REMOVABLE:
+
+ // CDROM is a removable media
+ case DRIVE_CDROM:
+
+ // no idea if ramdisks can change serial numbers or not
+ // but it doesn't hurt to treat them as removable.
+
+ case DRIVE_RAMDISK:
+
+
+ // Here is where we keep track of removable drives.
+ dwRemoveableDrivesToCheck |= 1 << dwDriveIndex;
+
+ // removable drives fall through to fixed drives and get cached.
+
+ case DRIVE_FIXED:
+
+ // cache volume serial numbers.
+ if (GetVolumeInformation(
+ &lpBuffer[dwIndex],
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwLastCachedDrive < dwDriveIndex)
+ dwLastCachedDrive = dwDriveIndex;
+ dwCachedVolumeSerialNumbers[dwDriveIndex] = dwVolumeSerialNumber;
+ }
+
+ break;
+ }
+ }
+
+ dwIndex += lstrlen(&lpBuffer[dwIndex]) +1;
+ }
+
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ return PR_TRUE;
+}
+
+PRInt32 IsFileLocal(HANDLE hFile)
+{
+ DWORD dwIndex = 0, dwMask;
+ BY_HANDLE_FILE_INFORMATION Info;
+ TCHAR szDrive[4] = TEXT("C:\\");
+ DWORD dwVolumeSerialNumber;
+ DWORD oldmode = (DWORD) -1;
+ int rv = _PR_REMOTE_FILE;
+
+ if (!GetFileInformationByHandle(hFile, &Info))
+ return -1;
+
+ // look to see if the volume serial number has been cached.
+ _MD_LOCK(&cachedVolumeLock);
+ while(dwIndex <= dwLastCachedDrive)
+ if (dwCachedVolumeSerialNumbers[dwIndex++] == Info.dwVolumeSerialNumber)
+ return _PR_LOCAL_FILE;
+ _MD_UNLOCK(&cachedVolumeLock);
+
+ // volume serial number not found in the cache. Check removable files.
+ // removable drives are noted as a bitmask. If the bit associated with
+ // a specific drive is set, then we should query its volume serial number
+ // as its possible it has changed.
+ dwMask = dwRemoveableDrivesToCheck;
+ dwIndex = 0;
+
+ while(dwMask)
+ {
+ while(!(dwMask & 1))
+ {
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ szDrive[0] = TEXT('A')+ (TCHAR) dwIndex;
+
+ // Calling GetVolumeInformation on a removeable drive where the
+ // disk is currently removed will cause a dialog box to the
+ // console. This is not good.
+ // Temporarily disable the SEM_FAILCRITICALERRORS to avoid the
+ // dialog.
+
+ oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ if (GetVolumeInformation(
+ szDrive,
+ NULL, 0,
+ &dwVolumeSerialNumber,
+ NULL, NULL, NULL, 0)
+ )
+ {
+ if (dwVolumeSerialNumber == Info.dwVolumeSerialNumber)
+ {
+ _MD_LOCK(&cachedVolumeLock);
+ if (dwLastCachedDrive < dwIndex)
+ dwLastCachedDrive = dwIndex;
+ dwCachedVolumeSerialNumbers[dwIndex] = dwVolumeSerialNumber;
+ _MD_UNLOCK(&cachedVolumeLock);
+ rv = _PR_LOCAL_FILE;
+ }
+ }
+ if (oldmode != (DWORD) -1) {
+ SetErrorMode(oldmode);
+ oldmode = (DWORD) -1;
+ }
+
+ if (rv == _PR_LOCAL_FILE)
+ return _PR_LOCAL_FILE;
+
+ dwIndex++;
+ dwMask = dwMask >> 1;
+ }
+
+ return _PR_REMOTE_FILE;
+}
+#endif /* _NEED_351_FILE_LOCKING_HACK */
+
+void PR_NT_UseNonblock()
+{
+ _nt_use_async = 0;
+}
+
+
+PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ while ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+ } else if (timeout == PR_INTERVAL_NO_WAIT) {
+ if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1)
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ return(rv);
+ } else {
+retry:
+ if ((rv = accept(osfd, (struct sockaddr *) addr, len)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+
+ rv = select(osfd + 1, &rd, NULL, NULL, tvp);
+ if (rv > 0) {
+ goto retry;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ } else
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _nt_nonblock_recv(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recv(osfd,buf,len,0)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32 _nt_nonblock_send(PRFileDesc *fd, char *buf, int len, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = send(osfd,buf,len,0)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < len)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32 _nt_nonblock_writev(PRFileDesc *fd, PRIOVec *iov, int size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index<size; index++) {
+ rv = _nt_nonblock_send(fd, iov[index].iov_base, iov[index].iov_len, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len ) {
+ if (rv <= 0) {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0)) {
+ return sent;
+ } else {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+
+ return sent;
+}
+
+PRInt32 _nt_nonblock_sendto(
+ PRFileDesc *fd, const char *buf, int len,
+ const struct sockaddr *addr, int addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < len) {
+ while ((rv = sendto(osfd,buf,len,0, addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ } else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < len)) {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT ) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32 _nt_nonblock_recvfrom(PRFileDesc *fd, char *buf, int len, struct sockaddr *addr, int *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recvfrom(osfd,buf,len,0,addr, addrlen)) == -1) {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking)) {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if ((rv = select(osfd + 1, &rd, NULL,NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0) {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ } else {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+/*
+ * UDP support: the continuation thread functions and recvfrom and sendto.
+ */
+
+static void pt_InsertTimedInternal(pt_Continuation *op)
+{
+ PRInt32 delta = 0;
+ pt_Continuation *t_op = NULL;
+ PRIntervalTime now = PR_IntervalNow(), op_tmo, qd_tmo;
+
+ /*
+ * If this element operation isn't timed, it gets queued at the
+ * end of the list (just after pt_tq.tail) and we're
+ * finishd early.
+ */
+ if (PR_INTERVAL_NO_TIMEOUT == op->timeout)
+ {
+ t_op = pt_tq.tail; /* put it at the end */
+ goto done;
+ }
+
+ /*
+ * The rest of this routine actaully deals with timed ops.
+ */
+
+ if (NULL != pt_tq.op)
+ {
+ /*
+ * To find where in the list to put the new operation, form
+ * the absolute time the operations in question will expire.
+ *
+ * The new operation ('op') will expire at now() + op->timeout.
+ *
+ * The operation that will time out furthest in the future will
+ * do so at pt_tq.epoch + pt_tq.op->timeout.
+ *
+ * Subsequently earlier timeouts are computed based on the latter
+ * knowledge by subracting the timeout deltas that are stored in
+ * the operation list. There are operation[n]->timeout ticks
+ * between the expiration of operation[n-1] and operation[n].e e
+ *
+ * Therefore, the operation[n-1] will expire operation[n]->timeout
+ * ticks prior to operation[n].
+ *
+ * This should be easy!
+ */
+ t_op = pt_tq.op; /* running pointer to queued op */
+ op_tmo = now + op->timeout; /* that's in absolute ticks */
+ qd_tmo = pt_tq.epoch + t_op->timeout; /* likewise */
+
+ do
+ {
+ /*
+ * If 'op' expires later than t_op, then insert 'op' just
+ * ahead of t_op. Otherwise, compute when operation[n-1]
+ * expires and try again.
+ *
+ * The actual different between the expiriation of 'op'
+ * and the current operation what becomes the new operaton's
+ * timeout interval. That interval is also subtracted from
+ * the interval of the operation immediately following where
+ * we stick 'op' (unless the next one isn't timed). The new
+ * timeout assigned to 'op' takes into account the values of
+ * now() and when the previous intervals were compured.
+ */
+ delta = op_tmo - qd_tmo;
+ if (delta >= 0)
+ {
+ op->timeout += (now - pt_tq.epoch);
+ goto done;
+ }
+
+ qd_tmo -= t_op->timeout; /* previous operaton expiration */
+ t_op = t_op->prev; /* point to previous operation */
+ if (NULL != t_op) qd_tmo += t_op->timeout;
+ } while (NULL != t_op);
+
+ /*
+ * If we got here we backed off the head of the list. That means that
+ * this timed entry has to go at the head of the list. This is just
+ * about like having an empty timer list.
+ */
+ delta = op->timeout; /* $$$ is this right? */
+ }
+
+done:
+
+ /*
+ * Insert 'op' into the queue just after t_op or if t_op is null,
+ * at the head of the list.
+ *
+ * If t_op is NULL, the list is currently empty and this is pretty
+ * easy.
+ */
+ if (NULL == t_op)
+ {
+ op->prev = NULL;
+ op->next = pt_tq.head;
+ pt_tq.head = op;
+ if (NULL == pt_tq.tail) pt_tq.tail = op;
+ else op->next->prev = op;
+ }
+ else
+ {
+ op->prev = t_op;
+ op->next = t_op->next;
+ if (NULL != op->prev)
+ op->prev->next = op;
+ if (NULL != op->next)
+ op->next->prev = op;
+ if (t_op == pt_tq.tail)
+ pt_tq.tail = op;
+ }
+
+ /*
+ * Are we adjusting our epoch, etc? Are we replacing
+ * what was previously the element due to expire furthest
+ * out in the future? Is this even a timed operation?
+ */
+ if (PR_INTERVAL_NO_TIMEOUT != op->timeout)
+ {
+ if ((NULL == pt_tq.op) /* we're the one and only */
+ || (t_op == pt_tq.op)) /* we're replacing */
+ {
+ pt_tq.op = op;
+ pt_tq.epoch = now;
+ }
+ }
+
+ pt_tq.op_count += 1;
+
+} /* pt_InsertTimedInternal */
+
+/*
+ * function: pt_FinishTimed
+ *
+ * Takes the finished operation out of the timed queue. It
+ * notifies the initiating thread that the opertions is
+ * complete and returns to the caller the value of the next
+ * operation in the list (or NULL).
+ */
+static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op)
+{
+ pt_Continuation *next;
+
+ /* remove this one from the list */
+ if (NULL == op->prev) pt_tq.head = op->next;
+ else op->prev->next = op->next;
+ if (NULL == op->next) pt_tq.tail = op->prev;
+ else op->next->prev = op->prev;
+
+ /* did we happen to hit the timed op? */
+ if (op == pt_tq.op) pt_tq.op = op->prev;
+
+ next = op->next;
+ op->next = op->prev = NULL;
+ op->status = pt_continuation_done;
+
+ pt_tq.op_count -= 1;
+#if defined(DEBUG)
+ pt_debug.continuationsServed += 1;
+#endif
+ PR_NotifyCondVar(op->complete);
+
+ return next;
+} /* pt_FinishTimedInternal */
+
+static void ContinuationThread(void *arg)
+{
+ /* initialization */
+ fd_set readSet, writeSet, exceptSet;
+ struct timeval tv;
+ SOCKET *pollingList = 0; /* list built for polling */
+ PRIntn pollingListUsed; /* # entries used in the list */
+ PRIntn pollingListNeeded; /* # entries needed this time */
+ PRIntn pollingSlotsAllocated = 0; /* # entries available in list */
+ PRIntervalTime mx_select_ticks = PR_MillisecondsToInterval(PT_DEFAULT_SELECT_MSEC);
+
+ /* do some real work */
+ while (1)
+ {
+ PRIntn rv;
+ PRStatus status;
+ PRIntn pollIndex;
+ pt_Continuation *op;
+ PRIntervalTime now = PR_IntervalNow();
+ PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
+
+ PR_Lock(pt_tq.ml);
+ while (NULL == pt_tq.head)
+ {
+ status = PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT);
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+ }
+ pollingListNeeded = pt_tq.op_count;
+ PR_Unlock(pt_tq.ml);
+
+ /* Okay. We're history */
+ if ((PR_FAILURE == status)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break;
+
+ /*
+ * We are not holding the pt_tq.ml lock now, so more items may
+ * get added to pt_tq during this window of time. We hope
+ * that 10 more spaces in the polling list should be enough.
+ */
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptSet);
+ pollingListNeeded += 10;
+ if (pollingListNeeded > pollingSlotsAllocated)
+ {
+ if (NULL != pollingList) PR_DELETE(pollingList);
+ pollingList = PR_MALLOC(pollingListNeeded * sizeof(PRPollDesc));
+ PR_ASSERT(NULL != pollingList);
+ pollingSlotsAllocated = pollingListNeeded;
+ }
+
+#if defined(DEBUG)
+ if (pollingListNeeded > pt_debug.pollingListMax)
+ pt_debug.pollingListMax = pollingListUsed;
+#endif
+
+ /*
+ * Build up a polling list.
+ * This list is sorted on time. Operations that have been
+ * interrupted are completed and not included in the list.
+ * There is an assertion that the operation is in progress.
+ */
+ pollingListUsed = 0;
+ PR_Lock(pt_tq.ml);
+
+ for (op = pt_tq.head; NULL != op;)
+ {
+ if (pt_continuation_abort == op->status)
+ {
+ op->result.code = -1;
+ op->syserrno = WSAEINTR;
+ op = pt_FinishTimedInternal(op);
+ }
+ else
+ {
+ PR_ASSERT(pt_continuation_done != op->status);
+ op->status = pt_continuation_inprogress;
+ if (op->event & PR_POLL_READ) {
+ FD_SET(op->arg1.osfd, &readSet);
+ }
+ if (op->event & PR_POLL_WRITE) {
+ FD_SET(op->arg1.osfd, &writeSet);
+ }
+ if (op->event & PR_POLL_EXCEPT) {
+ FD_SET(op->arg1.osfd, &exceptSet);
+ }
+ pollingList[pollingListUsed] = op->arg1.osfd;
+ pollingListUsed += 1;
+ if (pollingListUsed == pollingSlotsAllocated) break;
+ op = op->next;
+ }
+ }
+
+ PR_Unlock(pt_tq.ml);
+
+ /*
+ * If 'op' isn't NULL at this point, then we didn't get to
+ * the end of the list. That means that more items got added
+ * to the list than we anticipated. So, forget this iteration,
+ * go around the horn again.
+ * One would hope this doesn't happen all that often.
+ */
+ if (NULL != op)
+ {
+#if defined(DEBUG)
+ pt_debug.predictionsFoiled += 1; /* keep track */
+#endif
+ continue; /* make it rethink things */
+ }
+
+ /* there's a chance that all ops got blown away */
+ if (NULL == pt_tq.head) continue;
+ /* if not, we know this is the shortest timeout */
+ timeout = pt_tq.head->timeout;
+
+ /*
+ * We don't want to wait forever on this poll. So keep
+ * the interval down. The operations, if they are timed,
+ * still have to timeout, while those that are not timed
+ * should persist forever. But they may be aborted. That's
+ * what this anxiety is all about.
+ */
+ if (timeout > mx_select_ticks) timeout = mx_select_ticks;
+
+ if (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)
+ pt_tq.head->timeout -= timeout;
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+
+ rv = select(0, &readSet, &writeSet, &exceptSet, &tv);
+
+ if (0 == rv) /* poll timed out - what about leading op? */
+ {
+ if (0 == pt_tq.head->timeout)
+ {
+ /*
+ * The leading element of the timed queue has timed
+ * out. Get rid of it. In any case go around the
+ * loop again, computing the polling list, checking
+ * for interrupted operations.
+ */
+ PR_Lock(pt_tq.ml);
+ do
+ {
+ pt_tq.head->result.code = -1;
+ pt_tq.head->syserrno = WSAETIMEDOUT;
+ op = pt_FinishTimedInternal(pt_tq.head);
+ } while ((NULL != op) && (0 == op->timeout));
+ PR_Unlock(pt_tq.ml);
+ }
+ continue;
+ }
+
+ if (-1 == rv && (WSAGetLastError() == WSAEINTR
+ || WSAGetLastError() == WSAEINPROGRESS))
+ {
+ continue; /* go around the loop again */
+ }
+
+ /*
+ * select() says that something in our list is ready for some more
+ * action or is an invalid fd. Find it, load up the operation and
+ * see what happens.
+ */
+
+ PR_ASSERT(rv > 0 || WSAGetLastError() == WSAENOTSOCK);
+
+
+ /*
+ * $$$ There's a problem here. I'm running the operations list
+ * and I'm not holding any locks. I don't want to hold the lock
+ * and do the operation, so this is really messed up..
+ *
+ * This may work out okay. The rule is that only this thread,
+ * the continuation thread, can remove elements from the list.
+ * Therefore, the list is at worst, longer than when we built
+ * the polling list.
+ */
+ op = pt_tq.head;
+ for (pollIndex = 0; pollIndex < pollingListUsed; ++pollIndex)
+ {
+ PRInt16 revents = 0;
+
+ PR_ASSERT(NULL != op);
+
+ /*
+ * This one wants attention. Redo the operation.
+ * We know that there can only be more elements
+ * in the op list than we knew about when we created
+ * the poll list. Therefore, we might have to skip
+ * a few ops to find the right one to operation on.
+ */
+ while (pollingList[pollIndex] != op->arg1.osfd )
+ {
+ op = op->next;
+ PR_ASSERT(NULL != op);
+ }
+
+ if (FD_ISSET(op->arg1.osfd, &readSet)) {
+ revents |= PR_POLL_READ;
+ }
+ if (FD_ISSET(op->arg1.osfd, &writeSet)) {
+ revents |= PR_POLL_WRITE;
+ }
+ if (FD_ISSET(op->arg1.osfd, &exceptSet)) {
+ revents |= PR_POLL_EXCEPT;
+ }
+
+ /*
+ * Sip over all those not in progress. They'll be
+ * pruned next time we build a polling list. Call
+ * the continuation function. If it reports completion,
+ * finish off the operation.
+ */
+ if (revents && (pt_continuation_inprogress == op->status)
+ && (op->function(op, revents)))
+ {
+ PR_Lock(pt_tq.ml);
+ op = pt_FinishTimedInternal(op);
+ PR_Unlock(pt_tq.ml);
+ }
+ }
+ }
+ if (NULL != pollingList) PR_DELETE(pollingList);
+} /* ContinuationThread */
+
+static int pt_Continue(pt_Continuation *op)
+{
+ PRStatus rv;
+ /* Finish filling in the blank slots */
+ op->status = pt_continuation_sumbitted;
+ op->complete = PR_NewCondVar(pt_tq.ml);
+
+ PR_Lock(pt_tq.ml); /* we provide the locking */
+
+ pt_InsertTimedInternal(op); /* insert in the structure */
+
+ PR_NotifyCondVar(pt_tq.new_op); /* notify the continuation thread */
+
+ while (pt_continuation_done != op->status) /* wait for completion */
+ {
+ rv = PR_WaitCondVar(op->complete, PR_INTERVAL_NO_TIMEOUT);
+ /*
+ * If we get interrupted, we set state the continuation thread will
+ * see and allow it to finish the I/O operation w/ error. That way
+ * the rule that only the continuation thread is removing elements
+ * from the list is still valid.
+ *
+ * Don't call interrupt on the continuation thread. That'll just
+ * piss him off. He's cycling around at least every mx_select_ticks
+ * anyhow and should notice the request in there.
+ */
+ if ((PR_FAILURE == rv)
+ && (PR_PENDING_INTERRUPT_ERROR == PR_GetError()))
+ op->status = pt_continuation_abort; /* our status */
+ }
+
+ PR_Unlock(pt_tq.ml); /* we provide the locking */
+
+ PR_DestroyCondVar(op->complete);
+
+ return op->result.code; /* and the primary answer */
+} /* pt_Continue */
+
+static PRBool pt_sendto_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn bytes = sendto(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags,
+ (struct sockaddr*)op->arg5.addr, sizeof(*(op->arg5.addr)));
+ op->syserrno = WSAGetLastError();
+ if (bytes > 0) /* this is progress */
+ {
+ char *bp = op->arg2.buffer;
+ bp += bytes; /* adjust the buffer pointer */
+ op->arg2.buffer = bp;
+ op->result.code += bytes; /* accumulate the number sent */
+ op->arg3.amount -= bytes; /* and reduce the required count */
+ return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE;
+ }
+ else return ((-1 == bytes) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_sendto_cont */
+
+static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents)
+{
+ PRIntn addr_len = sizeof(*(op->arg5.addr));
+ op->result.code = recvfrom(
+ op->arg1.osfd, op->arg2.buffer, op->arg3.amount,
+ op->arg4.flags, (struct sockaddr*)op->arg5.addr, &addr_len);
+ op->syserrno = WSAGetLastError();
+ return ((-1 == op->result.code) && (WSAEWOULDBLOCK == op->syserrno)) ?
+ PR_FALSE : PR_TRUE;
+} /* pt_recvfrom_cont */
+
+static PRInt32 pt_SendTo(
+ SOCKET osfd, const void *buf,
+ PRInt32 amount, PRInt32 flags, const PRNetAddr *addr,
+ PRIntn addrlen, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = sendto(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, PR_NETADDR_SIZE(addr));
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ fNeedContinue = PR_TRUE;
+ else
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = (void*)buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.result.code = 0; /* initialize the number sent */
+ op.function = pt_sendto_cont;
+ op.event = PR_POLL_WRITE | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_SENDTO_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_SendTo */
+
+static PRInt32 pt_RecvFrom(SOCKET osfd, void *buf, PRInt32 amount,
+ PRInt32 flags, PRNetAddr *addr, PRIntn *addr_len, PRIntervalTime timeout)
+{
+ PRInt32 bytes = -1, err;
+ PRBool fNeedContinue = PR_FALSE;
+
+ bytes = recvfrom(
+ osfd, buf, amount, flags,
+ (struct sockaddr*)addr, addr_len);
+ if (bytes == -1) {
+ if ((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ fNeedContinue = PR_TRUE;
+ else
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+
+ if (fNeedContinue == PR_TRUE)
+ {
+ pt_Continuation op;
+ op.arg1.osfd = osfd;
+ op.arg2.buffer = buf;
+ op.arg3.amount = amount;
+ op.arg4.flags = flags;
+ op.arg5.addr = addr;
+ op.timeout = timeout;
+ op.function = pt_recvfrom_cont;
+ op.event = PR_POLL_READ | PR_POLL_EXCEPT;
+ bytes = pt_Continue(&op);
+ if (bytes < 0) {
+ WSASetLastError(op.syserrno);
+ _PR_MD_MAP_RECVFROM_ERROR(op.syserrno);
+ }
+ }
+ return bytes;
+} /* pt_RecvFrom */
diff --git a/pr/src/md/windows/ntmisc.c b/pr/src/md/windows/ntmisc.c
new file mode 100644
index 00000000..729bce4d
--- /dev/null
+++ b/pr/src/md/windows/ntmisc.c
@@ -0,0 +1,603 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * ntmisc.c
+ *
+ */
+
+#include "primpl.h"
+
+char *_PR_MD_GET_ENV(const char *name)
+{
+ return getenv(name);
+}
+
+PRIntn _PR_MD_PUT_ENV(const char *name)
+{
+ return putenv(name);
+}
+
+
+/*
+ **************************************************************************
+ **************************************************************************
+ **
+ ** Date and time routines
+ **
+ **************************************************************************
+ **************************************************************************
+ */
+
+#include <sys/timeb.h>
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for Windows.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+PRTime
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+/*
+ * The following code works around a bug in NT (Netscape Bugsplat
+ * Defect ID 47942).
+ *
+ * In Windows NT 3.51 and 4.0, if the local time zone does not practice
+ * daylight savings time, e.g., Arizona, Taiwan, and Japan, the global
+ * variables that _ftime() and localtime() depend on have the wrong
+ * default values:
+ * _tzname[0] "PST"
+ * _tzname[1] "PDT"
+ * _daylight 1
+ * _timezone 28800
+ *
+ * So at startup time, we need to invoke _PR_Win32InitTimeZone(), which
+ * on NT sets these global variables to the correct values (obtained by
+ * calling GetTimeZoneInformation().
+ */
+
+#include <time.h> /* for _tzname, _daylight, _timezone */
+
+void
+_PR_Win32InitTimeZone(void)
+{
+ OSVERSIONINFO version;
+ TIME_ZONE_INFORMATION tzinfo;
+
+ version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&version) != FALSE) {
+ /* Only Windows NT needs this hack */
+ if (version.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ return;
+ }
+ }
+
+ if (GetTimeZoneInformation(&tzinfo) == 0xffffffff) {
+ return; /* not much we can do if this failed */
+ }
+
+ /*
+ * I feel nervous about modifying these globals. I hope that no
+ * other thread is reading or modifying these globals simultaneously
+ * during nspr initialization.
+ *
+ * I am assuming that _tzname[0] and _tzname[1] point to static buffers
+ * and that the buffers are at least 32 byte long. My experiments show
+ * this is true, but of course this is undocumented. --wtc
+ *
+ * Convert time zone names from WCHAR to CHAR and copy them to
+ * the static buffers pointed to by _tzname[0] and _tzname[1].
+ * Ignore conversion errors, because it is _timezone and _daylight
+ * that _ftime() and localtime() really depend on.
+ */
+
+ WideCharToMultiByte(CP_ACP, 0, tzinfo.StandardName, -1, _tzname[0],
+ 32, NULL, NULL);
+ WideCharToMultiByte(CP_ACP, 0, tzinfo.DaylightName, -1, _tzname[1],
+ 32, NULL, NULL);
+
+ /* _timezone is in seconds. tzinfo.Bias is in minutes. */
+
+ _timezone = tzinfo.Bias * 60;
+ _daylight = tzinfo.DaylightBias ? 1 : 0;
+ return;
+}
+
+/*
+ ***********************************************************************
+ ***********************************************************************
+ *
+ * Process creation routines
+ *
+ ***********************************************************************
+ ***********************************************************************
+ */
+
+/*
+ * Assemble the command line by concatenating the argv array.
+ * On success, this function returns 0 and the resulting command
+ * line is returned in *cmdLine. On failure, it returns -1.
+ */
+static int assembleCmdLine(char *const *argv, char **cmdLine)
+{
+ char *const *arg;
+ char *p, *q;
+ int cmdLineSize;
+ int numBackslashes;
+ int i;
+ int argNeedQuotes;
+
+ /*
+ * Find out how large the command line buffer should be.
+ */
+ cmdLineSize = 0;
+ for (arg = argv; *arg; arg++) {
+ /*
+ * \ and " need to be escaped by a \. In the worst case,
+ * every character is a \ or ", so the string of length
+ * may double. If we quote an argument, that needs two ".
+ * Finally, we need a space between arguments, and
+ * a null byte at the end of command line.
+ */
+ cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */
+ + 2 /* we quote every argument */
+ + 1; /* space in between, or final null */
+ }
+ p = *cmdLine = PR_MALLOC(cmdLineSize);
+ if (p == NULL) {
+ return -1;
+ }
+
+ for (arg = argv; *arg; arg++) {
+ /* Add a space to separates the arguments */
+ if (arg != argv) {
+ *p++ = ' ';
+ }
+ q = *arg;
+ numBackslashes = 0;
+ argNeedQuotes = 0;
+
+ /* If the argument contains white space, it needs to be quoted. */
+ if (strpbrk(*arg, " \f\n\r\t\v")) {
+ argNeedQuotes = 1;
+ }
+
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ while (*q) {
+ if (*q == '\\') {
+ numBackslashes++;
+ q++;
+ } else if (*q == '"') {
+ if (numBackslashes) {
+ /*
+ * Double the backslashes since they are followed
+ * by a quote
+ */
+ for (i = 0; i < 2 * numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ /* To escape the quote */
+ *p++ = '\\';
+ *p++ = *q++;
+ } else {
+ if (numBackslashes) {
+ /*
+ * Backslashes are not followed by a quote, so
+ * don't need to double the backslashes.
+ */
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ numBackslashes = 0;
+ }
+ *p++ = *q++;
+ }
+ }
+
+ /* Now we are at the end of this argument */
+ if (numBackslashes) {
+ /*
+ * Double the backslashes if we have a quote string
+ * delimiter at the end.
+ */
+ if (argNeedQuotes) {
+ numBackslashes *= 2;
+ }
+ for (i = 0; i < numBackslashes; i++) {
+ *p++ = '\\';
+ }
+ }
+ if (argNeedQuotes) {
+ *p++ = '"';
+ }
+ }
+
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * Assemble the environment block by concatenating the envp array
+ * (preserving the terminating null byte in each array element)
+ * and adding a null byte at the end.
+ *
+ * Returns 0 on success. The resulting environment block is returned
+ * in *envBlock. Note that if envp is NULL, a NULL pointer is returned
+ * in *envBlock. Returns -1 on failure.
+ */
+static int assembleEnvBlock(char **envp, char **envBlock)
+{
+ char *p;
+ char *q;
+ char **env;
+ char *curEnv;
+ char *cwdStart, *cwdEnd;
+ int envBlockSize;
+
+ if (envp == NULL) {
+ *envBlock = NULL;
+ return 0;
+ }
+
+ curEnv = GetEnvironmentStrings();
+
+ cwdStart = curEnv;
+ while (*cwdStart) {
+ if (cwdStart[0] == '=' && cwdStart[1] != '\0'
+ && cwdStart[2] == ':' && cwdStart[3] == '=') {
+ break;
+ }
+ cwdStart += strlen(cwdStart) + 1;
+ }
+ cwdEnd = cwdStart;
+ if (*cwdEnd) {
+ cwdEnd += strlen(cwdEnd) + 1;
+ while (*cwdEnd) {
+ if (cwdEnd[0] != '=' || cwdEnd[1] == '\0'
+ || cwdEnd[2] != ':' || cwdEnd[3] != '=') {
+ break;
+ }
+ cwdEnd += strlen(cwdEnd) + 1;
+ }
+ }
+ envBlockSize = cwdEnd - cwdStart;
+
+ for (env = envp; *env; env++) {
+ envBlockSize += strlen(*env) + 1;
+ }
+ envBlockSize++;
+
+ p = *envBlock = PR_MALLOC(envBlockSize);
+ if (p == NULL) {
+ FreeEnvironmentStrings(curEnv);
+ return -1;
+ }
+
+ q = cwdStart;
+ while (q < cwdEnd) {
+ *p++ = *q++;
+ }
+ FreeEnvironmentStrings(curEnv);
+
+ for (env = envp; *env; env++) {
+ q = *env;
+ while (*q) {
+ *p++ = *q++;
+ }
+ *p++ = '\0';
+ }
+ *p = '\0';
+ return 0;
+}
+
+/*
+ * For qsort. We sort (case-insensitive) the environment strings
+ * before generating the environment block.
+ */
+static int compare(const void *arg1, const void *arg2)
+{
+ return _stricmp(* (char**)arg1, * (char**)arg2);
+}
+
+PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ STARTUPINFO startupInfo;
+ PROCESS_INFORMATION procInfo;
+ BOOL retVal;
+ char *cmdLine = NULL;
+ char *envBlock = NULL;
+ char **newEnvp;
+ PRProcess *proc = NULL;
+
+ proc = PR_NEW(PRProcess);
+ if (!proc) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (assembleCmdLine(argv, &cmdLine) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ if (envp == NULL) {
+ newEnvp = NULL;
+ } else {
+ int i;
+ int numEnv = 0;
+ while (envp[numEnv]) {
+ numEnv++;
+ }
+ newEnvp = (char **) PR_MALLOC((numEnv+1) * sizeof(char *));
+ for (i = 0; i <= numEnv; i++) {
+ newEnvp[i] = envp[i];
+ }
+ qsort((void *) newEnvp, (size_t) numEnv, sizeof(char *), compare);
+ }
+ if (assembleEnvBlock(newEnvp, &envBlock) == -1) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ goto errorExit;
+ }
+
+ ZeroMemory(&startupInfo, sizeof(startupInfo));
+ startupInfo.cb = sizeof(startupInfo);
+
+ if (attr) {
+ PRBool redirected = PR_FALSE;
+
+ /*
+ * XXX the default value for stdin, stdout, and stderr
+ * should probably be the console input and output, not
+ * those of the parent process.
+ */
+ startupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ startupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ if (attr->stdinFd) {
+ startupInfo.hStdInput = (HANDLE) attr->stdinFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (attr->stdoutFd) {
+ startupInfo.hStdOutput = (HANDLE) attr->stdoutFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (attr->stderrFd) {
+ startupInfo.hStdError = (HANDLE) attr->stderrFd->secret->md.osfd;
+ redirected = PR_TRUE;
+ }
+ if (redirected) {
+ startupInfo.dwFlags |= STARTF_USESTDHANDLES;
+ }
+ }
+
+ retVal = CreateProcess(NULL,
+ cmdLine,
+ NULL, /* security attributes for the new
+ * process */
+ NULL, /* security attributes for the primary
+ * thread in the new process */
+ TRUE, /* inherit handles */
+ 0, /* creation flags */
+ envBlock, /* an environment block, consisting
+ * of a null-terminated block of
+ * null-terminated strings. Each
+ * string is in the form:
+ * name=value
+ * XXX: usually NULL */
+ NULL, /* current drive and directory */
+ &startupInfo,
+ &procInfo
+ );
+ if (retVal == FALSE) {
+ /* XXX what error code? */
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ goto errorExit;
+ }
+
+ CloseHandle(procInfo.hThread);
+ proc->md.handle = procInfo.hProcess;
+ proc->md.id = procInfo.dwProcessId;
+
+ PR_DELETE(cmdLine);
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ return proc;
+
+errorExit:
+ if (cmdLine) {
+ PR_DELETE(cmdLine);
+ }
+ if (envBlock) {
+ PR_DELETE(envBlock);
+ }
+ if (proc) {
+ PR_DELETE(proc);
+ }
+ return NULL;
+} /* _PR_CreateWindowsProcess */
+
+PRStatus _PR_DetachWindowsProcess(PRProcess *process)
+{
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+/*
+ * XXX: This implementation is a temporary quick solution.
+ * It can be called by native threads only (not by fibers).
+ */
+PRStatus _PR_WaitWindowsProcess(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ DWORD dwRetVal;
+
+ dwRetVal = WaitForSingleObject(process->md.handle, INFINITE);
+ if (dwRetVal == WAIT_FAILED) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ PR_ASSERT(dwRetVal == WAIT_OBJECT_0);
+ if (exitCode != NULL &&
+ GetExitCodeProcess(process->md.handle, exitCode) == FALSE) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ CloseHandle(process->md.handle);
+ PR_DELETE(process);
+ return PR_SUCCESS;
+}
+
+PRStatus _PR_KillWindowsProcess(PRProcess *process)
+{
+ /*
+ * On Unix, if a process terminates normally, its exit code is
+ * between 0 and 255. So here on Windows, we use the exit code
+ * 256 to indicate that the process is killed.
+ */
+ if (TerminateProcess(process->md.handle, 256)) {
+ return PR_SUCCESS;
+ }
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+PRStatus _MD_WindowsGetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ syserror = WSAGetLastError();
+ PR_ASSERT(WSANOTINITIALISED != syserror);
+ _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
+ return PR_FAILURE;
+}
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ DWORD dwHi, dwLo;
+ DWORD flProtect;
+
+ dwLo = (DWORD) (size & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff);
+
+ if (fmap->prot == PR_PROT_READONLY) {
+ flProtect = PAGE_READONLY;
+ fmap->md.dwAccess = FILE_MAP_READ;
+ } else if (fmap->prot == PR_PROT_READWRITE) {
+ flProtect = PAGE_READWRITE;
+ fmap->md.dwAccess = FILE_MAP_WRITE;
+ } else {
+ PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY);
+ flProtect = PAGE_WRITECOPY;
+ fmap->md.dwAccess = FILE_MAP_COPY;
+ }
+
+ fmap->md.hFileMap = CreateFileMapping(
+ (HANDLE) fmap->fd->secret->md.osfd,
+ NULL,
+ flProtect,
+ dwHi,
+ dwLo,
+ NULL);
+
+ if (fmap->md.hFileMap == NULL) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ DWORD dwHi, dwLo;
+ void *addr;
+
+ dwLo = (DWORD) (offset & 0xffffffff);
+ dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff);
+ if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess,
+ dwHi, dwLo, len)) == NULL) {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ }
+ return addr;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ if (UnmapViewOfFile(addr)) {
+ return PR_SUCCESS;
+ } else {
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ CloseHandle(fmap->md.hFileMap);
+ PR_DELETE(fmap);
+ return PR_SUCCESS;
+}
diff --git a/pr/src/md/windows/ntsem.c b/pr/src/md/windows/ntsem.c
new file mode 100644
index 00000000..5d2f6c03
--- /dev/null
+++ b/pr/src/md/windows/ntsem.c
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * NT-specific semaphore handling code.
+ *
+ */
+
+
+#include "primpl.h"
+
+
+void
+_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value)
+{
+ md->sem = CreateSemaphore(NULL, value, 0x7fffffff, NULL);
+}
+
+void
+_PR_MD_DESTROY_SEM(_MDSemaphore *md)
+{
+ CloseHandle(md->sem);
+}
+
+PRStatus
+_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks)
+{
+ int rv;
+
+ rv = WaitForSingleObject(md->sem, PR_IntervalToMilliseconds(ticks));
+
+ if (rv == WAIT_OBJECT_0)
+ return PR_SUCCESS;
+ else
+ return PR_FAILURE;
+}
+
+PRStatus
+_PR_MD_WAIT_SEM(_MDSemaphore *md)
+{
+ return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT);
+}
+
+void
+_PR_MD_POST_SEM(_MDSemaphore *md)
+{
+ int old_count;
+
+ ReleaseSemaphore(md->sem, 1, &old_count);
+}
diff --git a/pr/src/md/windows/ntthread.c b/pr/src/md/windows/ntthread.c
new file mode 100644
index 00000000..885a0953
--- /dev/null
+++ b/pr/src/md/windows/ntthread.c
@@ -0,0 +1,434 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+extern void _PR_Win32InitTimeZone(void); /* defined in ntmisc.c */
+
+/* --- globals ------------------------------------------------ */
+PRLock *_pr_schedLock = NULL;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+__declspec(thread) PRThread *_pr_current_fiber;
+__declspec(thread) PRThread *_pr_fiber_last_run;
+__declspec(thread) _PRCPU *_pr_current_cpu;
+__declspec(thread) PRUintn _pr_ints_off;
+
+_MDLock _nt_idleLock;
+PRCList _nt_idleList;
+PRUint32 _nt_idleCount;
+
+extern __declspec(thread) PRThread *_pr_io_restarted_io;
+
+/* Must check the restarted_io *before* decrementing no_sched to 0 */
+#define POST_SWITCH_WORK() \
+ if (_pr_io_restarted_io) \
+ _nt_handle_restarted_io(_pr_io_restarted_io); \
+ _PR_MD_LAST_THREAD()->no_sched = 0;
+
+void
+_nt_handle_restarted_io(PRThread *restarted_io)
+{
+ /* After the switch we can resume an IO if needed.
+ * XXXMB - this needs to be done in create thread, since that could
+ * be the result for a context switch too..
+ */
+ PR_ASSERT(restarted_io->io_suspended == PR_TRUE);
+
+ _PR_THREAD_LOCK(restarted_io);
+ if (restarted_io->io_pending == PR_FALSE) {
+
+ /* The IO already completed, put us back on the runq. */
+ int pri = restarted_io->priority;
+
+ restarted_io->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(restarted_io->cpu);
+ _PR_ADD_RUNQ(restarted_io, restarted_io->cpu, pri);
+ _PR_RUNQ_UNLOCK(restarted_io->cpu);
+ } else {
+ _PR_SLEEPQ_LOCK(restarted_io->cpu);
+ _PR_ADD_SLEEPQ(restarted_io, restarted_io->sleep);
+ _PR_SLEEPQ_UNLOCK(restarted_io->cpu);
+ }
+ restarted_io->io_suspended = PR_FALSE;
+
+ _PR_THREAD_UNLOCK(restarted_io);
+
+ _pr_io_restarted_io = NULL;
+}
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _MD_NEW_LOCK( &_nt_idleLock );
+ _nt_idleCount = 0;
+ PR_INIT_CLIST(&_nt_idleList);
+ _PR_Win32InitTimeZone();
+
+#if 0
+ /* Make the clock tick at least once per millisecond */
+ if ( timeBeginPeriod(1) == TIMERR_NOCANDO) {
+ /* deep yoghurt; clock doesn't tick fast enough! */
+ PR_ASSERT(0);
+ }
+#endif
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+}
+
+void
+_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
+{
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread. GetCurrentThread()
+ ** returns a pseudo-handle which is not suitable for some thread
+ ** operations (ie. suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ DuplicateHandle( GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS ); /* Options */
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+#if 0
+ thread->md.handle = CreateThread(
+ NULL, /* security attrib */
+ thread->stack->stackSize, /* stack size */
+ (LPTHREAD_START_ROUTINE)start, /* startup routine */
+ (void *)thread, /* thread param */
+ CREATE_SUSPENDED, /* create flags */
+ &(thread->id) ); /* thread id */
+#else
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ (void *)thread,
+ CREATE_SUSPENDED,
+ &(thread->id));
+#endif
+ if(!thread->md.handle) {
+ PRErrorCode prerror;
+ thread->md.fiber_last_error = GetLastError();
+ switch (errno) {
+ case ENOMEM:
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case EAGAIN:
+ prerror = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+ case EINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, errno);
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1)
+ return PR_SUCCESS;
+
+ PR_SetError(PR_UNKNOWN_ERROR, GetLastError());
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+#if 0
+ /* XXXMB - does this work? Should we really set the priorities of
+ * native threads? */
+ if( newPri < 4 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_IDLE;
+ } else if( newPri < 8 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_LOWEST;
+ } else if( newPri < 12 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_BELOW_NORMAL;
+ } else if( newPri < 16 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_NORMAL;
+ } else if( newPri < 24 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_ABOVE_NORMAL;
+ } else if( newPri < 28 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_HIGHEST;
+ } else if( newPri < 32 ) {
+ newPri = (PRUintn)THREAD_PRIORITY_TIME_CRITICAL;
+ }
+
+ if( ! SetThreadPriority( thread->handle, newPri ) ) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+#endif
+
+ return;
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+
+ /* Don't call DeleteFiber on current fiber or we'll kill the whole thread.
+ * Don't call free(thread) until we've switched off the thread.
+ * So put this fiber (or thread) on a list to be deleted by the idle
+ * fiber next time we have a chance.
+ */
+ if (!(thread->flags & (_PR_ATTACHED|_PR_GLOBAL_SCOPE))) {
+ _MD_LOCK(&_nt_idleLock);
+ _nt_idleCount++;
+ PR_APPEND_LINK(&thread->links, &_nt_idleList);
+ _MD_UNLOCK(&_nt_idleLock);
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ if (thread->md.acceptex_buf) {
+ PR_DELETE(thread->md.acceptex_buf);
+ }
+
+ if (thread->md.xmit_bufs) {
+ PR_DELETE(thread->md.xmit_bufs);
+ }
+
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+
+ if (thread->flags & _PR_GLOBAL_SCOPE) {
+ _MD_SET_CURRENT_THREAD(NULL);
+ }
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+#ifdef HAVE_FIBERS
+
+void
+_pr_fiber_mainline(void *unused)
+{
+ PRThread *fiber = _PR_MD_CURRENT_THREAD();
+
+ POST_SWITCH_WORK();
+
+ fiber->md.fiber_fn(fiber->md.fiber_arg);
+}
+
+PRThread *_PR_MD_CREATE_USER_THREAD(
+ PRUint32 stacksize, void (*start)(void *), void *arg)
+{
+ PRThread *thread;
+
+ if ( (thread = PR_NEW(PRThread)) == NULL ) {
+ return NULL;
+ }
+
+ memset(thread, 0, sizeof(PRThread));
+ thread->md.fiber_fn = start;
+ thread->md.fiber_arg = arg;
+ thread->md.fiber_stacksize = stacksize;
+ return thread;
+}
+
+void
+_PR_MD_CREATE_PRIMORDIAL_USER_THREAD(PRThread *thread)
+{
+ thread->md.fiber_id = ConvertThreadToFiber(NULL);
+ PR_ASSERT(thread->md.fiber_id);
+ thread->flags &= (~_PR_GLOBAL_SCOPE);
+ _MD_SET_CURRENT_THREAD(thread);
+ _MD_SET_LAST_THREAD(thread);
+ thread->no_sched = 1;
+ return;
+}
+
+void
+_PR_MD_INIT_CONTEXT(PRThread *thread, char *top, void (*start) (void), PRBool *status)
+{
+ thread->md.fiber_fn = (void (*)(void *))start;
+ thread->md.fiber_id = CreateFiber(thread->md.fiber_stacksize,
+ (LPFIBER_START_ROUTINE)_pr_fiber_mainline, NULL);
+ if (thread->md.fiber_id != 0)
+ *status = PR_TRUE;
+ else {
+ DWORD oserror = GetLastError();
+ PRErrorCode prerror;
+ if (oserror == ERROR_NOT_ENOUGH_MEMORY) {
+ prerror = PR_OUT_OF_MEMORY_ERROR;
+ } else {
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, oserror);
+ *status = PR_FALSE;
+ }
+}
+
+void
+_PR_MD_SWITCH_CONTEXT(PRThread *thread)
+{
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ thread->md.fiber_last_error = GetLastError();
+ _PR_Schedule();
+}
+
+void
+_PR_MD_RESTORE_CONTEXT(PRThread *thread)
+{
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) );
+
+ /* The user-level code for yielding will happily add ourselves to the runq
+ * and then switch to ourselves; the NT fibers can't handle switching to
+ * ourselves.
+ */
+ if (thread != me) {
+ SetLastError(thread->md.fiber_last_error);
+ _MD_SET_CURRENT_THREAD(thread);
+ _PR_MD_SET_LAST_THREAD(me);
+ thread->no_sched = 1;
+ SwitchToFiber(thread->md.fiber_id);
+ POST_SWITCH_WORK();
+ }
+}
+
+
+#endif /* HAVE_FIBERS */
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ /*
+ ** There seems to be some doubt about whether or not SuspendThread
+ ** is a synchronous function. The test afterwards is to help veriry
+ ** that it is, which is what Microsoft says it is.
+ */
+ PRUintn rv = SuspendThread(thread->md.handle);
+ PR_ASSERT(0xffffffffUL != rv);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ ResumeThread(thread->md.handle);
+ }
+}
+
diff --git a/pr/src/md/windows/w16callb.c b/pr/src/md/windows/w16callb.c
new file mode 100644
index 00000000..d25f7c19
--- /dev/null
+++ b/pr/src/md/windows/w16callb.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** w16callb.c -- Implement Win16 Callback functions
+**
+** Functions here are to replace functions normally in
+** LIBC which are not implemented in MSVC's LIBC.
+** Some clients of NSPR expect to statically link
+** to NSPR and get these functions.
+**
+** Some are implemented as callbacks to the .EXE
+** some are implemented directly in this module.
+**
+*/
+
+#include "primpl.h"
+#include "windowsx.h"
+
+/*
+** _pr_callback_funcs -- This is where clients register the
+** callback function structure.
+*/
+struct PRMethodCallbackStr * _pr_callback_funcs;
+
+/*
+** PR_MDInitWin16() -- Register the PRMethodCallback table pointer
+**
+*/
+void PR_MDRegisterCallbacks(struct PRMethodCallbackStr *f)
+{
+ _pr_callback_funcs = f;
+}
+
+/*
+** NSPR re-implenentations of various C runtime functions:
+*/
+
+/*
+** PR_MD_printf() -- exported as printf()
+**
+*/
+int PR_MD_printf(const char *fmt, ...)
+{
+ char buffer[1024];
+ int ret = 0;
+ va_list args;
+
+ va_start(args, fmt);
+
+#ifdef DEBUG
+ PR_vsnprintf(buffer, sizeof(buffer), fmt, args);
+ {
+ if (_pr_callback_funcs != NULL && _pr_callback_funcs->auxOutput != NULL) {
+ (* _pr_callback_funcs->auxOutput)(buffer);
+ } else {
+ OutputDebugString(buffer);
+ }
+ }
+#endif
+
+ va_end(args);
+ return ret;
+}
+
+/*
+** PR_MD_sscanf() -- exported as sscanf()
+**
+*/
+int PR_MD_sscanf(const char *buf, const char *fmt, ...)
+{
+ int retval;
+ va_list arglist;
+
+ va_start(arglist, fmt);
+ retval = vsscanf((const unsigned char *)buf, (const unsigned char *)fmt, arglist);
+ va_end(arglist);
+ return retval;
+}
+
+/*
+** PR_MD_strftime() -- exported as strftime
+**
+*/
+size_t PR_MD_strftime(char *s, size_t len, const char *fmt, const struct tm *p)
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->strftime)(s, len, fmt, p);
+ } else {
+ PR_ASSERT(0);
+ return 0;
+ }
+}
+
+
+/*
+** PR_MD_malloc() -- exported as malloc()
+**
+*/
+void *PR_MD_malloc( size_t size )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->malloc)( size );
+ } else {
+ return GlobalAllocPtr(GPTR, (DWORD)size);
+ }
+} /* end malloc() */
+
+/*
+** PR_MD_calloc() -- exported as calloc()
+**
+*/
+void *PR_MD_calloc( size_t n, size_t size )
+{
+ void *p;
+ size_t sz;
+
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->calloc)( n, size );
+ } else {
+ sz = n * size;
+ p = GlobalAllocPtr(GPTR, (DWORD)sz );
+ memset( p, 0x00, sz );
+ return p;
+ }
+} /* end calloc() */
+
+/*
+** PR_MD_realloc() -- exported as realloc()
+**
+*/
+void *PR_MD_realloc( void* old_blk, size_t size )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->realloc)( old_blk, size );
+ } else {
+ return GlobalReAllocPtr( old_blk, (DWORD)size, GPTR);
+ }
+} /* end realloc */
+
+/*
+** PR_MD_free() -- exported as free()
+**
+*/
+void PR_MD_free( void *ptr )
+{
+ if( _pr_callback_funcs ) {
+ (*_pr_callback_funcs->free)( ptr );
+ return;
+ } else {
+ GlobalFreePtr( ptr );
+ return;
+ }
+} /* end free() */
+
+/*
+** PR_MD_getenv() -- exported as getenv()
+**
+*/
+char *PR_MD_getenv( const char *name )
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->getenv)( name );
+ } else {
+ return 0;
+ }
+} /* end getenv() */
+
+
+/*
+** PR_MD_perror() -- exported as perror()
+**
+** well, not really (lth. 12/5/97).
+** XXX hold this thought.
+**
+*/
+void PR_MD_perror( const char *prefix )
+{
+ return;
+} /* end perror() */
+
+/*
+** PR_MD_putenv() -- exported as putenv()
+**
+*/
+int PR_MD_putenv(const char *assoc)
+{
+ if( _pr_callback_funcs ) {
+ return (*_pr_callback_funcs->putenv)(assoc);
+ } else {
+ PR_ASSERT(0);
+ return NULL;
+ }
+}
+
+/*
+** PR_MD_fprintf() -- exported as fprintf()
+**
+*/
+int PR_MD_fprintf(FILE *fPtr, const char *fmt, ...)
+{
+ char buffer[1024];
+ va_list args;
+
+ va_start(args, fmt);
+ PR_vsnprintf(buffer, sizeof(buffer), fmt, args);
+
+ if (fPtr == NULL)
+ {
+ if (_pr_callback_funcs != NULL && _pr_callback_funcs->auxOutput != NULL)
+ {
+ (* _pr_callback_funcs->auxOutput)(buffer);
+ }
+ else
+ {
+ OutputDebugString(buffer);
+ }
+ }
+ else
+ {
+ fwrite(buffer, 0, strlen(buffer), fPtr); /* XXX Is this a sec. hole? */
+ }
+
+ va_end(args);
+ return 0;
+}
+
+/* end w16callb.c */
diff --git a/pr/src/md/windows/w16error.c b/pr/src/md/windows/w16error.c
new file mode 100644
index 00000000..f3ef99ae
--- /dev/null
+++ b/pr/src/md/windows/w16error.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** Note: A single error mapping function is provided.
+**
+*/
+#include "prerror.h"
+#include <errno.h>
+#include <winsock.h>
+
+
+void _PR_MD_map_error( int err )
+{
+
+ switch ( err )
+ {
+ case ENOENT: /* No such file or directory */
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case E2BIG: /* Argument list too big */
+ PR_SetError( PR_INVALID_ARGUMENT_ERROR, err );
+ break;
+ case ENOEXEC: /* Exec format error */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EBADF: /* Bad file number */
+ PR_SetError( PR_BAD_DESCRIPTOR_ERROR, err );
+ break;
+ case ENOMEM: /* Not enough Memory */
+ PR_SetError( PR_OUT_OF_MEMORY_ERROR, err );
+ break;
+ case EACCES: /* Permission denied */
+ PR_SetError( PR_NO_ACCESS_RIGHTS_ERROR, err );
+ break;
+ case EEXIST: /* File exists */
+
+ /* RESTART here */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EXDEV: /* Cross device link */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EINVAL: /* Invalid argument */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENFILE: /* File table overflow */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EMFILE: /* Too many open files */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOSPC: /* No space left on device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* math errors */
+ case EDOM: /* Argument too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ERANGE: /* Result too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* file locking error */
+ case EDEADLK: /* Resource deadlock would occur */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EINTR: /* Interrupt */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ECHILD: /* Child does not exist */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ /* POSIX errors */
+ case EAGAIN: /* Resource unavailable, try again */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EBUSY: /* Device or Resource is busy */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EFBIG: /* File too large */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EIO: /* I/O error */
+ PR_SetError( PR_IO_ERROR, err );
+ break;
+ case EISDIR: /* Is a directory */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTDIR: /* Not a directory */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EMLINK: /* Too many links */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTBLK: /* Block device required */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTTY: /* Not a character device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENXIO: /* No such device or address */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EPERM: /* Not owner */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EPIPE: /* Broken pipe */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EROFS: /* Read only file system */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ESPIPE: /* Illegal seek */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ESRCH: /* No such process */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ETXTBSY: /* Text file busy */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case EFAULT: /* Bad address */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENAMETOOLONG: /* Name too long */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENODEV: /* No such device */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOLCK: /* No locks available on system */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOSYS: /* Unknown system call */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ case ENOTEMPTY: /* Directory not empty */
+ /* Normative Addendum error */
+ case EILSEQ: /* Multibyte/widw character encoding error */
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+
+ /* WinSock errors */
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ case WSAENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ case WSAEMSGSIZE:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case WSAEPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case WSAEINTR:
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, err );
+ break;
+ case WSASYSNOTREADY:
+ case WSAVERNOTSUPPORTED:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+
+ default:
+ PR_SetError( PR_UNKNOWN_ERROR, err );
+ break;
+ }
+ return;
+} /* end _MD_map_win16_error() */
+
+
diff --git a/pr/src/md/windows/w16fmem.c b/pr/src/md/windows/w16fmem.c
new file mode 100644
index 00000000..836ca80b
--- /dev/null
+++ b/pr/src/md/windows/w16fmem.c
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+/*
+ **********************************************************************
+ *
+ * Memory-mapped files are not implemented on Win16.
+ *
+ **********************************************************************
+ */
+
+PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+void * _MD_MemMap(
+ PRFileMap *fmap,
+ PRInt64 offset,
+ PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _MD_MemUnmap(void *addr, PRUint32 len)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _MD_CloseFileMap(PRFileMap *fmap)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
diff --git a/pr/src/md/windows/w16gc.c b/pr/src/md/windows/w16gc.c
new file mode 100644
index 00000000..1a84bef4
--- /dev/null
+++ b/pr/src/md/windows/w16gc.c
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+PRWord *
+_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np)
+{
+ if (isCurrent)
+ {
+ _MD_SAVE_CONTEXT(t);
+ }
+ /*
+ ** In Win16 because the premption is "cooperative" it can never be the
+ ** case that a register holds the sole reference to an object. It
+ ** will always have been pushed onto the stack before the thread
+ ** switch... So don't bother to scan the registers...
+ */
+ *np = 0;
+
+ return (PRWord *) CONTEXT(t);
+}
+
+#if 0
+#ifndef SPORT_MODEL
+
+#define MAX_SEGMENT_SIZE (65536l - 4096l)
+
+/************************************************************************/
+/*
+** Machine dependent GC Heap management routines:
+** _MD_GrowGCHeap
+*/
+/************************************************************************/
+
+extern void *
+_MD_GrowGCHeap(uint32 *sizep)
+{
+ void *addr;
+
+ if( *sizep > MAX_SEGMENT_SIZE ) {
+ *sizep = MAX_SEGMENT_SIZE;
+ }
+
+ addr = malloc((size_t)*sizep);
+ return addr;
+}
+
+#endif /* SPORT_MODEL */
+#endif /* 0 */
+
diff --git a/pr/src/md/windows/w16io.c b/pr/src/md/windows/w16io.c
new file mode 100644
index 00000000..d5758893
--- /dev/null
+++ b/pr/src/md/windows/w16io.c
@@ -0,0 +1,836 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <share.h>
+#include <sys/locking.h>
+
+
+/*
+** Sleep this many milliseconds on each I/O operation
+** to cause an intentional thread switch.
+*/
+#define _PR_MD_WIN16_DELAY 1
+
+
+/*
+** PR_MD_RegisterW16StdioCallbacks() -- Register Win16 stdio callback functions
+**
+** This public function call is unique to Win16.
+** ... Sigh ... So much for platform independence.
+**
+** To get stdio to work from a command line executable, the stdio stream
+** calls must be issued from the .EXE file; calling them from the .DLL
+** sends the output to the bit-bucket. Therefore, the .EXE wanting to
+** do stdio to the console window (must be built as a "quickwin" application)
+** must have the wrapper functions defined in this module statically linked
+** into the .EXE.
+**
+** There appears to be nothing you can do to get stdio to work from a
+** Win16 GUI application. Oh Well!
+**
+*/
+PRStdinRead _pr_md_read_stdin = 0;
+PRStdoutWrite _pr_md_write_stdout = 0;
+PRStderrWrite _pr_md_write_stderr = 0;
+
+PRStatus
+PR_MD_RegisterW16StdioCallbacks( PRStdinRead inReadf, PRStdoutWrite outWritef, PRStderrWrite errWritef )
+{
+ _pr_md_write_stdout = outWritef;
+ _pr_md_write_stderr = errWritef;
+ _pr_md_read_stdin = inReadf;
+
+ return(PR_SUCCESS);
+} /* end PR_MD_RegisterW16StdioCallbacks() */
+
+
+/*
+** _PR_MD_OPEN() -- Open a file
+**
+** Returns: a fileHandle or -1
+**
+**
+*/
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ PRInt32 file;
+ int access = O_BINARY;
+ int rights = 0;
+
+
+ /*
+ ** Map NSPR open flags to os open flags
+ */
+ if (osflags & PR_RDONLY )
+ access |= O_RDONLY;
+ if (osflags & PR_WRONLY )
+ access |= O_WRONLY;
+ if (osflags & PR_RDWR )
+ access |= O_RDWR;
+ if (osflags & PR_CREATE_FILE )
+ {
+ access |= O_CREAT;
+ rights |= S_IRWXU;
+ }
+ if (osflags & PR_TRUNCATE)
+ access |= O_TRUNC;
+ if (osflags & PR_APPEND)
+ access |= O_APPEND;
+ else
+ access |= O_RDONLY;
+
+ /*
+ ** Open the file
+ */
+ file = (PRInt32) sopen( name, access, SH_DENYNO, rights );
+ if ( -1 == (PRInt32)file )
+ {
+ _PR_MD_MAP_OPEN_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return file;
+}
+
+/*
+** _PR_MD_READ() - Read something
+**
+** Returns: bytes read or -1
+**
+*/
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 rv;
+
+ if ( (PR_GetDescType(fd) == PR_DESC_FILE) &&
+ ( fd->secret->md.osfd == PR_StandardInput ) &&
+ ( _pr_md_write_stdout ))
+ {
+ rv = (*_pr_md_read_stdin)( buf, len);
+ }
+ else
+ {
+ rv = read( fd->secret->md.osfd, buf, len );
+ }
+
+ if ( rv == -1)
+ {
+ _PR_MD_MAP_READ_ERROR( errno );
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+/*
+** _PR_MD_WRITE() - Write something
+**
+** Returns: bytes written or -1
+**
+** Note: for file handles 1 and 2 (stdout and stderr)
+** call the Win16 NSPR stdio callback functions, if they are
+** registered.
+**
+*/
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ PRInt32 rv;
+
+ if ( (PR_GetDescType(fd) == PR_DESC_FILE))
+ {
+ switch ( fd->secret->md.osfd )
+ {
+ case PR_StandardOutput :
+ if ( _pr_md_write_stdout )
+ rv = (*_pr_md_write_stdout)( (void *)buf, len);
+ else
+ rv = len; /* fake success */
+ break;
+
+ case PR_StandardError :
+ if ( _pr_md_write_stderr )
+ rv = (*_pr_md_write_stderr)( (void *)buf, len);
+ else
+ rv = len; /* fake success */
+ break;
+
+ default:
+ rv = write( fd->secret->md.osfd, buf, len );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_WRITE_ERROR( errno );
+ }
+ break;
+ }
+ }
+ else
+ {
+ rv = write( fd->secret->md.osfd, buf, len );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_WRITE_ERROR( errno );
+ }
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* --- end _PR_MD_WRITE() --- */
+
+/*
+** _PR_MD_LSEEK() - Seek to position in a file
+**
+** Note: 'whence' maps directly to PR_...
+**
+** Returns:
+**
+*/
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = lseek( fd->secret->md.osfd, offset, whence );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_LSEEK64() -- Seek to position in file, 64bit offset.
+**
+*/
+PRInt64
+_PR_MD_LSEEK64( PRFileDesc *fd, PRInt64 offset, int whence )
+{
+ PRInt64 test;
+ PRInt32 rv, off;
+ LL_SHR(test, offset, 32);
+ if (!LL_IS_ZERO(test))
+ {
+ PR_SetError(PR_FILE_TOO_BIG_ERROR, 0);
+ LL_I2L(test, -1);
+ return test;
+ }
+ LL_L2I(off, offset);
+ rv = _PR_MD_LSEEK(fd, off, whence);
+ LL_I2L(test, rv);
+ return test;
+} /* end _PR_MD_LSEEK64() */
+
+/*
+** _PR_MD_FSYNC() - Flush file buffers.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) fsync( fd->secret->md.osfd );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_FSYNC_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_CLOSE() - Close an open file handle
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_CLOSE_FILE(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) close( osfd );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_CLOSE_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+} /* --- end _MD_CloseFile() --- */
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+
+/*
+** FlipSlashes() - Make forward slashes ('/') into backslashes
+**
+** Returns: void
+**
+**
+*/
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+
+/*
+** _PR_MD_OPEN_DIR() - Open a Directory.
+**
+** Returns:
+**
+**
+*/
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ d->dir = opendir( name );
+
+ if ( d->dir == NULL )
+ {
+ _PR_MD_MAP_OPENDIR_ERROR( errno );
+ return( PR_FAILURE );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( PR_SUCCESS );
+}
+
+
+/*
+** _PR_MD_READ_DIR() - read next directory entry
+**
+**
+*/
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ struct dirent *de;
+ int err;
+
+ for (;;)
+ {
+ de = readdir( d->dir );
+ if ( de == NULL ) {
+ _PR_MD_MAP_READDIR_ERROR( errno);
+ return 0;
+ }
+ if ((flags & PR_SKIP_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == 0))
+ continue;
+ if ((flags & PR_SKIP_DOT_DOT) &&
+ (de->d_name[0] == '.') && (de->d_name[1] == '.') &&
+ (de->d_name[2] == 0))
+ continue;
+ break;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return de->d_name;
+}
+
+/*
+** _PR_MD_CLOSE_DIR() - Close a directory.
+**
+**
+*/
+PRInt32
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ PRInt32 rv;
+
+ if ( d->dir )
+ {
+ rv = closedir( d->dir );
+ if (rv != 0)
+ {
+ _PR_MD_MAP_CLOSEDIR_ERROR( errno );
+ }
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+
+/*
+** _PR_MD_DELETE() - Delete a file.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) remove( name );
+ if ( rv != 0 )
+ {
+ _PR_MD_MAP_DELETE_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+
+/*
+** _PR_MD_STAT() - Get file attributes by filename
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_STAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_GETFILEINFO() - Get file attributes by filename
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ struct _stat sb;
+ PRInt32 rv;
+
+ if ( (rv = _stat(fn, &sb)) == 0 ) {
+ if (info) {
+ if (S_IFREG & sb.st_mode)
+ info->type = PR_FILE_FILE ;
+ else if (S_IFDIR & sb.st_mode)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = sb.st_size;
+ LL_I2L(info->modifyTime, sb.st_mtime);
+ LL_I2L(info->creationTime, sb.st_ctime);
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_STAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+
+ PRInt32 rv = _PR_MD_GETFILEINFO(fn, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ LL_I2L(info->size, info32.size);
+ }
+ return(rv);
+}
+
+/*
+** _PR_MD_GETOPENFILEINFO() - Get file attributes from an open file handle
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ struct stat statBuf;
+ PRInt32 rv = PR_SUCCESS;
+
+ rv = fstat( fd->secret->md.osfd, &statBuf );
+ if ( rv == 0)
+ {
+ if (statBuf.st_mode & S_IFREG )
+ info->type = PR_FILE_FILE;
+ else if ( statBuf.st_mode & S_IFDIR )
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_OTHER;
+ info->size = statBuf.st_size;
+ LL_I2L(info->modifyTime, statBuf.st_mtime);
+ LL_I2L(info->creationTime, statBuf.st_ctime);
+
+ }
+ else
+ {
+ _PR_MD_MAP_FSTAT_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ PRFileInfo info32;
+
+ PRInt32 rv = _PR_MD_GETOPENFILEINFO(fd, &info32);
+ if (0 == rv)
+ {
+ info->type = info32.type;
+ info->modifyTime = info32.modifyTime;
+ info->creationTime = info32.creationTime;
+ LL_I2L(info->size, info32.size);
+ }
+ return(rv);
+}
+
+/*
+** _PR_MD_RENAME() - Rename a file
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ PRInt32 rv;
+
+ rv = rename( from, to );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_RENAME_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return( rv );
+}
+
+/*
+** _PR_MD_ACCESS() - Return file acesss attribute.
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+ PRInt32 rv;
+ int mode = 0;
+
+ if ( how & PR_ACCESS_WRITE_OK )
+ mode |= W_OK;
+ if ( how & PR_ACCESS_READ_OK )
+ mode |= R_OK;
+
+ rv = (PRInt32) access( name, mode );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_ACCESS_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_MKDIR() - Make a directory
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ PRInt32 rv;
+
+ rv = mkdir( name );
+ if ( rv == 0 )
+ {
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_SUCCESS;
+ }
+ else
+ {
+ _PR_MD_MAP_MKDIR_ERROR( errno );
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_FAILURE;
+ }
+}
+
+/*
+** _PR_MD_RMDIR() - Delete a directory
+**
+** Returns:
+**
+**
+*/
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ PRInt32 rv;
+
+ rv = (PRInt32) rmdir( name );
+ if ( rv == -1 )
+ {
+ _PR_MD_MAP_RMDIR_ERROR( errno );
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return(rv);
+}
+
+/*
+** _PR_MD_LOCKFILE() - Lock a file.
+**
+** The _locking() call locks relative to the current file pointer.
+** This function is required to lock all of the file, so,
+** 1. Seek to the beginning of the file, preserving the original position.
+** 2. Lock the file, pausing if it is locked by someone else, and
+** try again.
+** 3. Re-position to the original position in the file.
+**
+** For unlocking, a similar protocol of positioning is required.
+**
+*/
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return to our caller */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* what the system call returns to us */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Attempt to lock the file.
+ ** If someone else has it, Sleep-a-while and try again.
+ */
+ for( rc = -1; rc != 0; )
+ {
+ rc = _locking( f, _LK_NBLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ if ( errno == EACCES )
+ {
+ PR_Sleep( 100 );
+ continue;
+ }
+ else
+ {
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ break;
+ }
+ }
+ } /* end for() */
+
+ /*
+ ** Now that the file is locked, re-position to
+ ** the original file position.
+ **
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+/*
+** _PR_MD_TLOCKFILE() - Test and Lock file.
+**
+** The _locking() call locks relative to the current file pointer.
+** This function is required to lock all of the file, so,
+** 1. Seek to the beginning of the file, preserving the original position.
+** 2. Attempt to Lock the file.
+** If the file is locked by someone else, try NO MORE.
+** 3. Re-position to the original position in the file.
+**
+** See the discussion of _PR_MD_LOCKFILE
+**
+**
+*/
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* return value from system call */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Attempt to lock the file. One ping; one ping only, Vasily.
+ ** If someone else has it, Reposition and return failure.
+ */
+ rc = _locking( f, _LK_NBLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ if ( errno != EACCES )
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ /*
+ ** Now that the file is locked, maybe, re-position to
+ ** the original file position.
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* end _PR_MD_TLOCKFILE() */
+
+
+/*
+** _PR_MD_UNLOCKFILE() - Unlock a file.
+**
+** See the discussion of _PR_MD_LOCKFILE
+**
+*/
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv = PR_SUCCESS; /* What we return */
+ long seekOrigin; /* original position in file */
+ PRInt32 rc; /* return value from system call */
+
+ /*
+ ** Seek to beginning of file, saving original position.
+ */
+ seekOrigin = lseek( f, 0l, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ return( PR_FAILURE );
+ }
+
+ /*
+ ** Unlock the file.
+ */
+ rc = _locking( f, _LK_UNLCK , 0x7fffffff );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LOCKF_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+
+ /*
+ ** Now that the file is unlocked, re-position to
+ ** the original file position.
+ */
+ rc = lseek( f, seekOrigin, SEEK_SET );
+ if ( rc == -1 )
+ {
+ _PR_MD_MAP_LSEEK_ERROR( errno );
+ rv = PR_FAILURE;
+ }
+ PR_Sleep( _PR_MD_WIN16_DELAY );
+ return rv;
+} /* end _PR_MD_UNLOCKFILE() */
+
+/*
+** PR_Stat() -- Return status on a file
+**
+** This is a hack! ... See BugSplat: 98516
+** Basically, this hack takes a name and stat buffer as input.
+** The input stat buffer is presumed to be a Microsoft stat buffer.
+** The functions does a Watcom stat() then maps the result to
+** the MS stat buffer. ...
+**
+*/
+PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf)
+{
+ PRInt32 rv;
+ _MDMSStat *mssb = (_MDMSStat*) buf; /* this is Microsoft's stat buffer */
+ struct stat statBuf; /* this is Watcom's stat buffer */
+
+ /* First, get Watcom's idea of stat
+ ** then reformat it into a Microsoft idea of stat
+ */
+ rv = (PRInt32) _stat( name, &statBuf);
+ if (rv == 0l )
+ {
+ mssb->st_dev = statBuf.st_dev;
+ mssb->st_ino = statBuf.st_ino; /* not used, really */
+ mssb->st_mode = statBuf.st_mode;
+ mssb->st_nlink = 1; /* always 1, says MS */
+ mssb->st_uid = statBuf.st_uid;
+ mssb->st_gid = statBuf.st_gid;
+ mssb->st_rdev = statBuf.st_rdev; /* please Gh0d! Let these be the same */
+ mssb->st_size = statBuf.st_size;
+ mssb->st_atime = statBuf.st_atime;
+ mssb->st_mtime = statBuf.st_mtime;
+ mssb->st_ctime = statBuf.st_ctime;
+ }
+ return rv;
+} /* end PR_Stat() */
+
+
+
+/* $$ end W16io.c */
diff --git a/pr/src/md/windows/w16mem.c b/pr/src/md/windows/w16mem.c
new file mode 100644
index 00000000..f1fe6a08
--- /dev/null
+++ b/pr/src/md/windows/w16mem.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*******************************************************************
+** w16mem.c -- Implement memory segment functions.
+**
+**
+********************************************************************
+*/
+#include "primpl.h"
+
+
+/*
+** Allocate a new memory segment.
+**
+** Return the segment's access rights and size.
+*/
+PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr)
+{
+ PR_ASSERT(seg != 0);
+ PR_ASSERT(size != 0);
+ PR_ASSERT(vaddr == 0);
+
+ /*
+ ** Take the actual memory for the segment out of our Figment heap.
+ */
+
+ seg->vaddr = (char *)malloc(size);
+
+ if (seg->vaddr == NULL) {
+ return PR_FAILURE;
+ }
+
+ seg->access = PR_SEGMENT_RDWR;
+ seg->size = size;
+
+ return PR_SUCCESS;
+} /* --- end _MD_AllocSegment() --- */
+
+
+/*
+** Free previously allocated memory segment.
+*/
+void _MD_FreeSegment(PRSegment *seg)
+{
+ PR_ASSERT((seg->flags & _PR_SEG_VM) == 0);
+
+ if (seg->vaddr != NULL)
+ free( seg->vaddr );
+ return;
+} /* --- end _MD_FreeSegment() --- */
diff --git a/pr/src/md/windows/w16null.c b/pr/src/md/windows/w16null.c
new file mode 100644
index 00000000..a32e83d6
--- /dev/null
+++ b/pr/src/md/windows/w16null.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+
+struct _MDLock _pr_ioq_lock;
+HINSTANCE _pr_hInstance = NULL;
+char * _pr_top_of_task_stack;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+/*
+ *-----------------------------------------------------------------------
+ *
+ * PR_Now --
+ *
+ * Returns the current time in microseconds since the epoch.
+ * The epoch is midnight January 1, 1970 GMT.
+ * The implementation is machine dependent. This is the
+ * implementation for Windows.
+ * Cf. time_t time(time_t *tp)
+ *
+ *-----------------------------------------------------------------------
+ */
+
+#if defined(HAVE_WATCOM_BUG_2)
+PRTime __pascal __export __loadds
+#else
+PRTime
+#endif
+PR_Now(void)
+{
+ PRInt64 s, ms, ms2us, s2us;
+ struct timeb b;
+
+ ftime(&b);
+ LL_I2L(ms2us, PR_USEC_PER_MSEC);
+ LL_I2L(s2us, PR_USEC_PER_SEC);
+ LL_I2L(s, b.time);
+ LL_I2L(ms, (PRInt32)b.millitm);
+ LL_MUL(ms, ms, ms2us);
+ LL_MUL(s, s, s2us);
+ LL_ADD(s, s, ms);
+ return s;
+}
+
+
+
+char *_PR_MD_GET_ENV(const char *name)
+{
+ return NULL;
+}
+
+PRIntn
+_PR_MD_PUT_ENV(const char *name)
+{
+ return NULL;
+}
+
+int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg,
+ WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ _pr_hInstance = hInst;
+ return TRUE;
+}
+
+
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _tzset();
+ return;
+}
+
+void
+_PR_MD_WAKEUP_CPUS( void )
+{
+ return;
+}
+
diff --git a/pr/src/md/windows/w16proc.c b/pr/src/md/windows/w16proc.c
new file mode 100644
index 00000000..16032aef
--- /dev/null
+++ b/pr/src/md/windows/w16proc.c
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+
+
+/*
+** Create Process.
+*/
+PRProcess * _PR_CreateWindowsProcess(
+ const char *path,
+ char *const *argv,
+ char *const *envp,
+ const PRProcessAttr *attr)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+PRStatus _PR_DetachWindowsProcess(PRProcess *process)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_WaitWindowsProcess(PRProcess *process,
+ PRInt32 *exitCode)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+PRStatus _PR_KillWindowsProcess(PRProcess *process)
+{
+ PR_ASSERT(!"Not implemented");
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
diff --git a/pr/src/md/windows/w16sock.c b/pr/src/md/windows/w16sock.c
new file mode 100644
index 00000000..13231324
--- /dev/null
+++ b/pr/src/md/windows/w16sock.c
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+
+static int winsockNotPresent = 0;
+
+void
+_PR_MD_INIT_IO()
+{
+ int rv;
+
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+
+ rv = WSAStartup( WSAVersion, &WSAData );
+ if ( rv != 0 )
+ {
+ _PR_MD_MAP_WSASTARTUP_ERROR(WSAGetLastError());
+ winsockNotPresent = 1;
+ }
+ return;
+}
+
+void
+_PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ int rv;
+ int err;
+
+ rv = WSACleanup();
+ if ( rv == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ PR_ASSERT(0);
+ }
+ return;
+} /* end _PR_MD_CLEANUP_BEFORE_EXIT() */
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+PRStatus
+_MD_WindowsGetHostName(char *name, PRUint32 namelen)
+{
+ PRIntn rv;
+ PRInt32 syserror;
+
+ rv = gethostname(name, (PRInt32) namelen);
+ if (0 == rv) {
+ return PR_SUCCESS;
+ }
+ syserror = WSAGetLastError();
+ PR_ASSERT(WSANOTINITIALISED != syserror);
+ _PR_MD_MAP_GETHOSTNAME_ERROR(syserror);
+ return PR_FAILURE;
+}
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ PRUint32 one = 1;
+ PRInt32 rv;
+ PRInt32 err;
+
+ if ( winsockNotPresent )
+ return( (PRInt32)INVALID_SOCKET );
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ int rv = GetLastError();
+ closesocket(sock);
+ _PR_MD_MAP_SOCKET_ERROR(rv);
+ return (PRInt32)INVALID_SOCKET;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ rv = ioctlsocket( sock, FIONBIO, &one);
+ if ( rv != 0 )
+ {
+ err = WSAGetLastError();
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+
+PRInt32
+_PR_MD_SOCKETAVAILABLE(PRFileDesc *fd)
+{
+ PRUint32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_PR_MD_CLOSE_SOCKET(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError());
+
+ return rv;
+}
+
+PRInt32 _PR_MD_LISTEN(PRFileDesc *fd, PRIntn backlog)
+{
+ int rv, err;
+
+ rv = listen(fd->secret->md.osfd, backlog);
+ if ( rv == SOCKET_ERROR ) {
+ _PR_MD_MAP_LISTEN_ERROR(WSAGetLastError());
+ return(-1);
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_ACCEPT(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen,
+ PRIntervalTime timeout )
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 err;
+ PRIntn rv;
+
+ MD_ASSERTINT( *addrlen );
+
+ while ((rv = (SOCKET)accept(osfd, (struct sockaddr *) addr,
+ (int *)addrlen)) == INVALID_SOCKET ) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+done:
+ if ( rv == INVALID_SOCKET )
+ return(-1 );
+ else
+ return(rv);
+} /* end _MD_Accept() */
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) {
+ err = WSAGetLastError();
+ if (err == WSAEISCONN) {
+ rv = 0;
+ break;
+ }
+ /* for winsock1.1, it reports EALREADY as EINVAL */
+ if ((err == WSAEWOULDBLOCK)
+ ||(err == WSAEALREADY)
+ || (err = WSAEINVAL)) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (rv < 0) {
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+ int one = 1;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = recv(osfd,buf,amount,flags)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECV_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = send(osfd,buf,amount,flags)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SEND_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc*fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((rv = sendto(osfd, buf, amount, flags,
+ (struct sockaddr *) addr, addrlen)) == -1) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ }
+done:
+ return rv;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRInt32 rv, err;
+
+ while ((*addrlen = PR_NETADDR_SIZE(addr)),
+ ((rv = recvfrom(osfd, buf, amount, flags,
+ (struct sockaddr FAR *) addr,(int FAR *)addrlen)) == -1)) {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) {
+ if (fd->secret->nonblocking) {
+ break;
+ }
+ if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) {
+ if ( _PR_PENDING_INTERRUPT(me))
+ {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ } else
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ rv = -1;
+ goto done;
+ } else if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0);
+ rv = -1;
+ goto done;
+ }
+ } else if ((err == WSAEINTR) && (!_PR_PENDING_INTERRUPT(me))){
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (rv < 0) {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ }
+done:
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+
+/*
+ * XXX To be fixed
+ * should call PR_Send
+ */
+
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (sent <= 0)
+ return -1;
+ return -1;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int *)len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, (int*)len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, (int*)optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_PR_MD_MAKE_NONBLOCK(PRFileDesc *f)
+{
+ return; // do nothing!
+}
+
+/*
+** Wait for I/O on a single descriptor.
+ *
+ * return 0, if timed-out, else return 1
+*/
+PRInt32
+_PR_WaitForFD(PRInt32 osfd, PRUintn how, PRIntervalTime timeout)
+{
+ _PRWin16PollDesc *pd;
+ PRPollQueue *pq;
+ PRIntn is;
+ PRInt32 rv = 1;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ PR_ASSERT(!(me->flags & _PR_IDLE_THREAD));
+
+ pd = &me->md.thr_pd;
+ pq = &me->md.thr_pq;
+ if (timeout == PR_INTERVAL_NO_WAIT) return 0;
+
+ pd->osfd = osfd;
+ pd->in_flags = how;
+ pd->out_flags = 0;
+
+ pq->pds = pd;
+ pq->npds = 1;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ return 0;
+ }
+
+ pq->thr = me;
+ pq->on_ioq = PR_TRUE;
+ pq->timeout = timeout;
+ _PR_ADD_TO_IOQ((*pq), me->cpu);
+ if (how == PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ (_PR_FD_READ_CNT(me->cpu))[osfd]++;
+ } else if (how == PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ } else {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ if (_PR_IOQ_TIMEOUT(me->cpu) > timeout)
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+ _PR_THREAD_LOCK(me);
+
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq->on_ioq) {
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq->on_ioq) {
+ PR_REMOVE_LINK(&pq->links);
+ if (how == PR_POLL_READ) {
+ if ((--(_PR_FD_READ_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+
+ } else if (how == PR_POLL_WRITE) {
+ if ((--(_PR_FD_WRITE_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ } else {
+ if ((--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]) == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ rv = 0;
+ }
+ _PR_FAST_INTSON(is);
+ return(rv);
+}
+
+/*
+ * Unblock threads waiting for I/O
+ * used when interrupting threads
+ *
+ * NOTE: The thread lock should held when this function is called.
+ * On return, the thread lock is released.
+ */
+void _PR_Unblock_IO_Wait(PRThread *thr)
+{
+ int pri = thr->priority;
+ _PRCPU *cpu = thr->cpu;
+
+ PR_ASSERT(thr->flags & (_PR_ON_SLEEPQ | _PR_ON_PAUSEQ));
+ _PR_SLEEPQ_LOCK(cpu);
+ _PR_DEL_SLEEPQ(thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(cpu);
+
+ PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD));
+ thr->state = _PR_RUNNABLE;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ _PR_THREAD_UNLOCK(thr);
+ _PR_MD_WAKEUP_WAITER(thr);
+}
+
+/*
+** Scan through io queue and find any bad fd's that triggered the error
+** from _MD_SELECT
+*/
+static void FindBadFDs(void)
+{
+ PRCList *q;
+ PRThread *me = _MD_CURRENT_THREAD();
+ int sockOpt;
+ int sockOptLen = sizeof(sockOpt);
+
+ PR_ASSERT(!_PR_IS_NATIVE_THREAD(me));
+ q = (_PR_IOQ(me->cpu)).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRWin16PollDesc *pds = pq->pds;
+ _PRWin16PollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ pds->out_flags = 0;
+ PR_ASSERT(osfd >= 0 || pds->in_flags == 0);
+ if (pds->in_flags == 0) {
+ continue; /* skip this fd */
+ }
+ if ( getsockopt(osfd,
+ (int)SOL_SOCKET,
+ SO_TYPE,
+ (char*)&sockOpt,
+ &sockOptLen) == SOCKET_ERROR )
+ {
+ if ( WSAGetLastError() == WSAENOTSOCK )
+ {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX,
+ ("file descriptor %d is bad", osfd));
+ pds->out_flags = PR_POLL_NVAL;
+ notify = PR_TRUE;
+ }
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+
+ if (notify) {
+ PRIntn pri;
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+
+ _PR_THREAD_LOCK(pq->thr);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = pq->thr->cpu;
+
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ }
+ _PR_THREAD_UNLOCK(pq->thr);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+} /* end FindBadFDs() */
+
+/*
+** Called by the scheduler when there is nothing to do. This means that
+** all threads are blocked on some monitor somewhere.
+**
+** Pause the current CPU. longjmp to the cpu's pause stack
+*/
+PRInt32 _PR_MD_PAUSE_CPU( PRIntervalTime ticks)
+{
+ PRThread *me = _MD_CURRENT_THREAD();
+ struct timeval timeout, *tvp;
+ fd_set r, w, e;
+ fd_set *rp, *wp, *ep;
+ PRInt32 max_osfd, nfd;
+ PRInt32 rv;
+ PRCList *q;
+ PRUint32 min_timeout;
+
+ PR_ASSERT(_PR_MD_GET_INTSOFF() != 0);
+
+ /*
+ * assigment of fd_sets
+ */
+ r = _PR_FD_READ_SET(me->cpu);
+ w = _PR_FD_WRITE_SET(me->cpu);
+ e = _PR_FD_EXCEPTION_SET(me->cpu);
+
+ rp = &r;
+ wp = &w;
+ ep = &e;
+
+ max_osfd = _PR_IOQ_MAX_OSFD(me->cpu) + 1;
+ min_timeout = _PR_IOQ_TIMEOUT(me->cpu);
+ /*
+ ** Compute the minimum timeout value: make it the smaller of the
+ ** timeouts specified by the i/o pollers or the timeout of the first
+ ** sleeping thread.
+ */
+ q = _PR_SLEEPQ(me->cpu).next;
+
+ if (q != &_PR_SLEEPQ(me->cpu)) {
+ PRThread *t = _PR_THREAD_PTR(q);
+
+ if (t->sleep < min_timeout) {
+ min_timeout = t->sleep;
+ }
+ }
+ if (min_timeout > ticks) {
+ min_timeout = ticks;
+ }
+
+ if (min_timeout == PR_INTERVAL_NO_TIMEOUT) {
+ tvp = NULL;
+ } else {
+ timeout.tv_sec = PR_IntervalToSeconds(min_timeout);
+ timeout.tv_usec = PR_IntervalToMicroseconds(min_timeout)
+ % PR_USEC_PER_SEC;
+ tvp = &timeout;
+ }
+
+ _PR_MD_IOQ_UNLOCK();
+ _MD_CHECK_FOR_EXIT();
+ /*
+ * check for i/o operations
+ */
+
+ nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp);
+
+ _MD_CHECK_FOR_EXIT();
+ _PR_MD_IOQ_LOCK();
+ /*
+ ** Notify monitors that are associated with the selected descriptors.
+ */
+ if (nfd > 0) {
+ q = _PR_IOQ(me->cpu).next;
+ _PR_IOQ_MAX_OSFD(me->cpu) = -1;
+ _PR_IOQ_TIMEOUT(me->cpu) = PR_INTERVAL_NO_TIMEOUT;
+ while (q != &_PR_IOQ(me->cpu)) {
+ PRPollQueue *pq = _PR_POLLQUEUE_PTR(q);
+ PRBool notify = PR_FALSE;
+ _PRWin16PollDesc *pds = pq->pds;
+ _PRWin16PollDesc *epds = pds + pq->npds;
+ PRInt32 pq_max_osfd = -1;
+
+ q = q->next;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PRInt16 out_flags = 0;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, rp)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, wp)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, ep)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pds->out_flags = out_flags;
+ if (out_flags) {
+ notify = PR_TRUE;
+ }
+ if (osfd > pq_max_osfd) {
+ pq_max_osfd = osfd;
+ }
+ }
+ if (notify == PR_TRUE) {
+ PRIntn pri;
+ PRThread *thred;
+
+ PR_REMOVE_LINK(&pq->links);
+ pq->on_ioq = PR_FALSE;
+
+ /*
+ * Decrement the count of descriptors for each desciptor/event
+ * because this I/O request is being removed from the
+ * ioq
+ */
+ pds = pq->pds;
+ for (; pds < epds; pds++) {
+ PRInt32 osfd = pds->osfd;
+ PRInt16 in_flags = pds->in_flags;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ thred = pq->thr;
+ _PR_THREAD_LOCK(thred);
+ if (pq->thr->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) {
+ _PRCPU *cpu = thred->cpu;
+ _PR_SLEEPQ_LOCK(pq->thr->cpu);
+ _PR_DEL_SLEEPQ(pq->thr, PR_TRUE);
+ _PR_SLEEPQ_UNLOCK(pq->thr->cpu);
+
+ pri = pq->thr->priority;
+ pq->thr->state = _PR_RUNNABLE;
+
+ pq->thr->cpu = cpu;
+ _PR_RUNQ_LOCK(cpu);
+ _PR_ADD_RUNQ(pq->thr, cpu, pri);
+ _PR_RUNQ_UNLOCK(cpu);
+ if (_pr_md_idle_cpus > 1)
+ _PR_MD_WAKEUP_WAITER(thred);
+ }
+ _PR_THREAD_UNLOCK(thred);
+ } else {
+ if (pq->timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = pq->timeout;
+ if (_PR_IOQ_MAX_OSFD(me->cpu) < pq_max_osfd)
+ _PR_IOQ_MAX_OSFD(me->cpu) = pq_max_osfd;
+ }
+ }
+ } else if (nfd < 0) {
+ if ( WSAGetLastError() == WSAENOTSOCK )
+ {
+ FindBadFDs();
+ } else {
+ PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d",
+ errno));
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ return(0);
+
+} /* end _PR_MD_PAUSE_CPU() */
+
+
+/*
+** _MD_pr_poll() -- Implement MD polling
+**
+** The function was snatched (re-used) from the unix implementation.
+**
+** The native thread stuff was deleted.
+** The pollqueue is instantiated on the mdthread structure
+** to keep the stack frame from being corrupted when this
+** thread is waiting on the poll.
+**
+*/
+extern PRInt32
+_MD_PR_POLL(PRPollDesc *pds, PRIntn npds,
+ PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ PRInt32 n, err, pdcnt;
+ PRIntn is;
+ _PRWin16PollDesc *spds, *spd;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+ PRPollQueue *pq;
+
+ pq = &me->md.thr_pq;
+
+ /*
+ * XXX
+ * PRPollDesc has a PRFileDesc field, fd, while the IOQ
+ * is a list of PRPollQueue structures, each of which contains
+ * a _PRWin16PollDesc. A _PRWin16PollDesc struct contains
+ * the OS file descriptor, osfd, and not a PRFileDesc.
+ * So, we have allocate memory for _PRWin16PollDesc structures,
+ * copy the flags information from the pds list and have pq
+ * point to this list of _PRWin16PollDesc structures.
+ *
+ * It would be better if the memory allocation can be avoided.
+ */
+
+ spds = (_PRWin16PollDesc*) PR_MALLOC(npds * sizeof(_PRWin16PollDesc));
+ if (!spds) {
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
+ return -1;
+ }
+ spd = spds;
+
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ _PR_THREAD_LOCK(me);
+
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+ PR_DELETE(spds);
+ return -1;
+ }
+
+ pdcnt = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+
+ spd->osfd = osfd;
+ spd->in_flags = pd->in_flags;
+ spd++;
+ pdcnt++;
+
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &_PR_FD_READ_SET(me->cpu));
+ _PR_FD_READ_CNT(me->cpu)[osfd]++;
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ (_PR_FD_WRITE_CNT(me->cpu))[osfd]++;
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ (_PR_FD_EXCEPTION_CNT(me->cpu))[osfd]++;
+ }
+ if (osfd > _PR_IOQ_MAX_OSFD(me->cpu))
+ _PR_IOQ_MAX_OSFD(me->cpu) = osfd;
+ }
+ if (timeout < _PR_IOQ_TIMEOUT(me->cpu))
+ _PR_IOQ_TIMEOUT(me->cpu) = timeout;
+
+
+ pq->pds = spds;
+ pq->npds = pdcnt;
+
+ pq->thr = me;
+ pq->on_ioq = PR_TRUE;
+ pq->timeout = timeout;
+ _PR_ADD_TO_IOQ((*pq), me->cpu);
+ _PR_SLEEPQ_LOCK(me->cpu);
+ _PR_ADD_SLEEPQ(me, timeout);
+ me->state = _PR_IO_WAIT;
+ me->io_pending = PR_TRUE;
+ me->io_suspended = PR_FALSE;
+ _PR_SLEEPQ_UNLOCK(me->cpu);
+ _PR_THREAD_UNLOCK(me);
+ _PR_MD_IOQ_UNLOCK();
+
+ _PR_MD_WAIT(me, timeout);
+
+ me->io_pending = PR_FALSE;
+ me->io_suspended = PR_FALSE;
+
+ /*
+ * Copy the out_flags from the _PRWin16PollDesc structures to the
+ * user's PRPollDesc structures and free the allocated memory
+ */
+ spd = spds;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ if ((NULL == pd->fd) || (pd->in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ pd->out_flags = spd->out_flags;
+ spd++;
+ }
+ PR_DELETE(spds);
+
+ /*
+ ** If we timed out the pollq might still be on the ioq. Remove it
+ ** before continuing.
+ */
+ if (pq->on_ioq) {
+ _PR_INTSOFF(is);
+ _PR_MD_IOQ_LOCK();
+ /*
+ * Need to check pq.on_ioq again
+ */
+ if (pq->on_ioq == PR_TRUE) {
+ PR_REMOVE_LINK(&pq->links);
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ PRInt32 osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = bottom->secret->md.osfd;
+ PR_ASSERT(osfd >= 0 || in_flags == 0);
+ if (in_flags & PR_POLL_READ) {
+ if (--(_PR_FD_READ_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_READ_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ if (--(_PR_FD_WRITE_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_WRITE_SET(me->cpu));
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ if (--(_PR_FD_EXCEPTION_CNT(me->cpu))[osfd] == 0)
+ FD_CLR(osfd, &_PR_FD_EXCEPTION_SET(me->cpu));
+ }
+ }
+ }
+ _PR_MD_IOQ_UNLOCK();
+ _PR_INTSON(is);
+ }
+ if (_PR_PENDING_INTERRUPT(me)) {
+ me->flags &= ~_PR_INTERRUPT;
+ PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0);
+ return -1;
+ } else {
+ n = 0;
+ if (pq->on_ioq == PR_FALSE) {
+ /* Count the number of ready descriptors */
+ while (--npds >= 0) {
+ if (pds->out_flags) {
+ n++;
+ }
+ pds++;
+ }
+ }
+ return n;
+ }
+} /* end _MD_pr_poll() */
diff --git a/pr/src/md/windows/w16stdio.c b/pr/src/md/windows/w16stdio.c
new file mode 100644
index 00000000..cdd420ae
--- /dev/null
+++ b/pr/src/md/windows/w16stdio.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+** w16stdio.c -- Callback functions for Win16 stdio read/write.
+**
+**
+*/
+#include "primpl.h"
+
+/*
+** _PL_MDStdioWrite() -- Win16 hackery to get console output
+**
+** Returns: number of bytes written.
+**
+*/
+PRInt32
+_PL_W16StdioWrite( void *buf, PRInt32 amount )
+{
+ int rc;
+
+ rc = fputs( buf, stdout );
+ if ( rc == EOF )
+ {
+ // something about errno
+ return(PR_FAILURE);
+ }
+ return( strlen(buf));
+} /* end _PL_fputs() */
+
+/*
+** _PL_W16StdioRead() -- Win16 hackery to get console input
+**
+*/
+PRInt32
+_PL_W16StdioRead( void *buf, PRInt32 amount )
+{
+ char *bp;
+
+ bp = fgets( buf, (int) amount, stdin );
+ if ( bp == NULL )
+ {
+ // something about errno
+ return(PR_FAILURE);
+ }
+
+ return( strlen(buf));
+} /* end _PL_fgets() */
+/* --- end w16stdio.c --- */
+
+/*
+** Wrappers, linked into the client, that call
+** functions in LibC
+**
+*/
+
+/*
+** _PL_W16CallBackPuts() -- Wrapper for puts()
+**
+*/
+int PR_CALLBACK _PL_W16CallBackPuts( const char *outputString )
+{
+ return( puts( outputString ));
+} /* end _PL_W16CallBackPuts() */
+
+/*
+** _PL_W16CallBackStrftime() -- Wrapper for strftime()
+**
+*/
+size_t PR_CALLBACK _PL_W16CallBackStrftime(
+ char *s,
+ size_t len,
+ const char *fmt,
+ const struct tm *p )
+{
+ return( strftime( s, len, fmt, p ));
+} /* end _PL_W16CallBackStrftime() */
+
+/*
+** _PL_W16CallBackMalloc() -- Wrapper for malloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackMalloc( size_t size )
+{
+ return( malloc( size ));
+} /* end _PL_W16CallBackMalloc() */
+
+/*
+** _PL_W16CallBackCalloc() -- Wrapper for calloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackCalloc( size_t n, size_t size )
+{
+ return( calloc( n, size ));
+} /* end _PL_W16CallBackCalloc() */
+
+/*
+** _PL_W16CallBackRealloc() -- Wrapper for realloc()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackRealloc(
+ void *old_blk,
+ size_t size )
+{
+ return( realloc( old_blk, size ));
+} /* end _PL_W16CallBackRealloc() */
+
+/*
+** _PL_W16CallBackFree() -- Wrapper for free()
+**
+*/
+void PR_CALLBACK _PL_W16CallBackFree( void *ptr )
+{
+ free( ptr );
+ return;
+} /* end _PL_W16CallBackFree() */
+
+/*
+** _PL_W16CallBackGetenv() -- Wrapper for getenv()
+**
+*/
+void * PR_CALLBACK _PL_W16CallBackGetenv( const char *name )
+{
+ return( getenv( name ));
+} /* end _PL_W16CallBackGetenv */
+
+
+/*
+** _PL_W16CallBackPutenv() -- Wrapper for putenv()
+**
+*/
+int PR_CALLBACK _PL_W16CallBackPutenv( const char *assoc )
+{
+ return( putenv( assoc ));
+} /* end _PL_W16CallBackGetenv */
diff --git a/pr/src/md/windows/w16thred.c b/pr/src/md/windows/w16thred.c
new file mode 100644
index 00000000..f6716759
--- /dev/null
+++ b/pr/src/md/windows/w16thred.c
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <sys/timeb.h>
+#include <stdio.h>
+
+/*
+** DispatchTrace -- define a thread dispatch trace entry
+**
+** The DispatchTrace oject(s) are instantiated in a single
+** array. Think of the array as a push-down stack; entry
+** zero is the most recent, entry one the next most recent, etc.
+** For each time PR_MD_RESTORE_CONTEXT() is called, the array
+** is Pushed down and entry zero is overwritten with data
+** for the newly dispatched thread.
+**
+** Function TraceDispatch() manages the DispatchTrace array.
+**
+*/
+typedef struct DispatchTrace
+{
+ PRThread * thread;
+ PRUint32 state;
+ PRInt16 mdThreadNumber;
+ PRInt16 unused;
+ PRThreadPriority priority;
+
+} DispatchTrace, *DispatchTracePtr ;
+
+static void TraceDispatch( PRThread *thread );
+
+
+PRThread *_pr_primordialThread;
+
+/*
+** Note: the static variables must be on the data-segment because
+** the stack is destroyed during shadow-stack copy operations.
+**
+*/
+static char * pSource; /* ptr to sourc of a "shadow-stack" copy */
+static char * pTarget; /* ptr to target of a "shadow-stack" copy */
+static int cxByteCount; /* number of bytes for "shadow-stack" copy */
+static int bytesMoved; /* instrumentation: WRT "shadow-stack" copy */
+static FILE * file1 = 0; /* instrumentation: WRT debug */
+
+#define NUM_DISPATCHTRACE_OBJECTS 24
+static DispatchTrace dt[NUM_DISPATCHTRACE_OBJECTS] = {0}; /* instrumentation: WRT dispatch */
+static PRUint32 dispatchCount = 0; /* instrumentation: number of thread dispatches */
+
+static int OldPriorityOfPrimaryThread = -1;
+static int TimeSlicesOnNonPrimaryThread = 0;
+static PRUint32 threadNumber = 1; /* Instrumentation: monotonically increasing number */
+
+
+
+/*
+** _PR_MD_FINAL_INIT() -- Final MD Initialization
+**
+** Poultry Problems! ... The stack, as allocated by PR_NewStack()
+** is called from here, late in initialization, because PR_NewStack()
+** requires lots of things working. When some elements of the
+** primordial thread are created, early in initialization, the
+** shadow stack is not one of these things. The "shadow stack" is
+** created here, late in initiailization using PR_NewStack(), to
+** ensure consistency in creation of the related objects.
+**
+** A new ThreadStack, and all its affiliated structures, is allocated
+** via the call to PR_NewStack(). The PRThread structure in the
+** new stack is ignored; the old PRThread structure is used (why?).
+** The old PRThreadStack structure is abandoned.
+**
+*/
+void
+_PR_MD_FINAL_INIT()
+{
+ PRThreadStack * stack = 0;
+ PRInt32 stacksize = 0;
+ PRThread * me = _PR_MD_CURRENT_THREAD();
+
+ _PR_ADJUST_STACKSIZE( stacksize );
+ stack = _PR_NewStack( stacksize );
+
+ me->stack = stack;
+ stack->thr = me;
+
+ return;
+} /* --- end _PR_MD_FINAL_INIT() --- */
+
+
+void
+_MD_INIT_RUNNING_CPU( struct _PRCPU *cpu )
+{
+ PR_INIT_CLIST(&(cpu->md.ioQ));
+ cpu->md.ioq_max_osfd = -1;
+ cpu->md.ioq_timeout = PR_INTERVAL_NO_TIMEOUT;
+}
+
+
+void
+_PR_MD_YIELD( void )
+{
+ PR_ASSERT(0);
+}
+
+/*
+** _PR_MD_INIT_STACK() -- Win16 specific Stack initialization.
+**
+**
+*/
+
+void
+_PR_MD_INIT_STACK( PRThreadStack *ts, PRIntn redzone )
+{
+ ts->md.stackTop = ts->stackTop - sizeof(PRThread);
+ ts->md.cxByteCount = 0;
+
+ return;
+} /* --- end _PR_MD_INIT_STACK() --- */
+
+/*
+** _PR_MD_INIT_THREAD() -- Win16 specific Thread initialization.
+**
+*/
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ if ( thread->flags & _PR_PRIMORDIAL)
+ {
+ _pr_primordialThread = thread;
+ thread->md.threadNumber = 1;
+ }
+ else
+ {
+ thread->md.threadNumber = ++threadNumber;
+ }
+
+ thread->md.magic = _MD_MAGIC_THREAD;
+ strcpy( thread->md.guardBand, "GuardBand" );
+
+ return PR_SUCCESS;
+}
+
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ _MD_SWITCH_CONTEXT( thread );
+
+ return( PR_SUCCESS );
+}
+
+void *PR_W16GetExceptionContext(void)
+{
+ return _MD_CURRENT_THREAD()->md.exceptionContext;
+}
+
+void
+PR_W16SetExceptionContext(void *context)
+{
+ _MD_CURRENT_THREAD()->md.exceptionContext = context;
+}
+
+
+
+
+/*
+** _MD_RESTORE_CONTEXT() -- Resume execution of thread 't'.
+**
+** Win16 threading is based on the NSPR 2.0 general model of
+** user threads. It differs from the general model in that a
+** single "real" stack segment is used for execution of all
+** threads. The context of the suspended threads is preserved
+** in the md.context [and related members] of the PRThread
+** structure. The stack context of the suspended thread is
+** preserved in a "shadow stack" object.
+**
+** _MD_RESTORE_CONTEXT() implements most of the thread switching
+** for NSPR's implementation of Win16 theads.
+**
+** Operations Notes:
+**
+** Function PR_NewStack() in prustack.c allocates a new
+** PRThreadStack, PRStack, PRSegment, and a "shadow" stack
+** for a thread. These structures are wired together to
+** form the basis of Win16 threads. The thread and shadow
+** stack structures are created as part of PR_CreateThread().
+**
+** Note! Some special "magic" is applied to the "primordial"
+** thread. The physical layout of the PRThread, PRThreadStack,
+** shadow stack, ... is somewhat different. Watch yourself when
+** mucking around with it. ... See _PR_MD_FINAL_INIT() for most
+** of the special treatment of the primordial thread.
+**
+** Function _PR_MD_INIT_STACK() initializes the value of
+** PRThreadStack member md.cxByteCount to zero; there
+** is no context to be restored for a thread's initial
+** dispatch. The value of member md.stackTop is set to
+** point to the highest usable address on the shadow stack.
+** This point corresponds to _pr_top_of_task_stack on the
+** system's operating stack.
+**
+** _pr_top_of_task_stack points to a place on the system stack
+** considered to be "close to the top". Stack context is preserved
+** relative to this point.
+**
+** Reminder: In x86 architecture, the stack grows "down".
+** That is: the stack pointer (SP register) is decremented
+** to push objects onto the stack or when a call is made.
+**
+** Function _PR_MD_WAIT() invokes macro _MD_SWITCH_CONTEXT();
+** this causes the hardware registers to be preserved in a
+** CATCHBUF structure using function Catch() [see _win16.h],
+** then calls PR_Schedule() to select a new thread for dispatch.
+** PR_Schedule() calls _MD_RESTORE_CONTEXT() to cause the thread
+** being suspended's stack to be preserved, to restore the
+** stack of the to-be-dispactched thread, and to restore the
+** to-be-dispactched thread's hardware registers.
+**
+** At the moment _PR_MD_RESTORE_CONTEXT() is called, the stack
+** pointer (SP) is less than the reference pointer
+** _pr_top_of_task_stack. The distance difference between the SP and
+** _pr_top_of_task_stack is the amount of stack that must be preserved.
+** This value, cxByteCount, is calculated then preserved in the
+** PRThreadStack.md.cxByteCount for later use (size of stack
+** context to restore) when this thread is dispatched again.
+**
+** A C language for() loop is used to copy, byte-by-byte, the
+** stack data being preserved starting at the "address of t"
+** [Note: 't' is the argument passed to _PR_MD_RESTORE_CONTEXT()]
+** for the length of cxByteCount.
+**
+** variables pSource and pTarget are the calculated source and
+** destination pointers for the stack copy operation. These
+** variables are static scope because they cannot be instantiated
+** on the stack itself, since the stack is clobbered by restoring
+** the to-be-dispatched thread's stack context.
+**
+** After preserving the suspended thread's stack and architectural
+** context, the to-be-dispatched thread's stack context is copied
+** from its shadow stack to the system operational stack. The copy
+** is done in a small fragment of in-line assembly language. Note:
+** In NSPR 1.0, a while() loop was used to do the copy; when compiled
+** with the MS C 1.52c compiler, the short while loop used no
+** stack variables. The Watcom compiler, specified for use on NSPR 2.0,
+** uses stack variables to implement the same while loop. This is
+** a no-no! The copy operation clobbers these variables making the
+** results of the copy ... unpredictable ... So, a short piece of
+** inline assembly language is used to effect the copy.
+**
+** Following the restoration of the to-be-dispatched thread's
+** stack context, another short inline piece of assemble language
+** is used to set the SP register to correspond to what it was
+** when the to-be-dispatched thread was suspended. This value
+** uses the thread's stack->md.cxByteCount as a negative offset
+** from _pr_top_of_task_stack as the new value of SP.
+**
+** Finally, Function Throw() is called to restore the architectural
+** context of the to-be-dispatched thread.
+**
+** At this point, the newly dispatched thread appears to resume
+** execution following the _PR_MD_SWITCH_CONTEXT() macro.
+**
+** OK, this ain't rocket-science, but it can confuse you easily.
+** If you have to work on this stuff, please take the time to
+** draw, on paper, the structures (PRThread, PRThreadStack,
+** PRSegment, the "shadow stack", the system stack and the related
+** global variables). Hand step it thru the debugger to make sure
+** you understand it very well before making any changes. ...
+** YMMV.
+**
+*/
+void _MD_RESTORE_CONTEXT(PRThread *t)
+{
+ dispatchCount++;
+ TraceDispatch( t );
+ /*
+ ** This is a good opportunity to make sure that the main
+ ** mozilla thread actually gets some time. If interrupts
+ ** are on, then we know it is safe to check if the main
+ ** thread is being starved. If moz has not been scheduled
+ ** for a long time, then then temporarily bump the fe priority
+ ** up so that it gets to run at least one.
+ */
+// #if 0 // lth. condition off for debug.
+ if (_pr_primordialThread == t) {
+ if (OldPriorityOfPrimaryThread != -1) {
+ PR_SetThreadPriority(_pr_primordialThread, OldPriorityOfPrimaryThread);
+ OldPriorityOfPrimaryThread = -1;
+ }
+ TimeSlicesOnNonPrimaryThread = 0;
+ } else {
+ TimeSlicesOnNonPrimaryThread++;
+ }
+
+ if ((TimeSlicesOnNonPrimaryThread >= 20) && (OldPriorityOfPrimaryThread == -1)) {
+ OldPriorityOfPrimaryThread = PR_GetThreadPriority(_pr_primordialThread);
+ PR_SetThreadPriority(_pr_primordialThread, 31);
+ TimeSlicesOnNonPrimaryThread = 0;
+ }
+// #endif
+ /*
+ ** Save the Task Stack into the "shadow stack" of the current thread
+ */
+ cxByteCount = (int) ((PRUint32) _pr_top_of_task_stack - (PRUint32) &t );
+ pSource = (char *) &t;
+ pTarget = (char *)((PRUint32)_pr_currentThread->stack->md.stackTop
+ - (PRUint32)cxByteCount );
+ _pr_currentThread->stack->md.cxByteCount = cxByteCount;
+
+ for( bytesMoved = 0; bytesMoved < cxByteCount; bytesMoved++ )
+ *(pTarget + bytesMoved ) = *(pSource + bytesMoved );
+
+ /* Mark the new thread as the current thread */
+ _pr_currentThread = t;
+
+ /*
+ ** Now copy the "shadow stack" of the new thread into the Task Stack
+ **
+ ** REMEMBER:
+ ** After the stack has been copied, ALL local variables in this function
+ ** are invalid !!
+ */
+ cxByteCount = t->stack->md.cxByteCount;
+ pSource = t->stack->md.stackTop - cxByteCount;
+ pTarget = _pr_top_of_task_stack - cxByteCount;
+
+ errno = (_pr_currentThread)->md.errcode;
+
+ __asm
+ {
+ mov cx, cxByteCount
+ mov si, WORD PTR [pSource]
+ mov di, WORD PTR [pTarget]
+ mov ax, WORD PTR [pTarget + 2]
+ mov es, ax
+ mov ax, WORD PTR [pSource + 2]
+ mov bx, ds
+ mov ds, ax
+ rep movsb
+ mov ds, bx
+ }
+
+ /*
+ ** IMPORTANT:
+ ** ----------
+ ** SS:SP is now invalid :-( This means that all local variables and
+ ** function arguments are invalid and NO function calls can be
+ ** made !!! We must fix up SS:SP so that function calls can safely
+ ** be made...
+ */
+
+ __asm {
+ mov ax, WORD PTR [_pr_top_of_task_stack]
+ sub ax, cxByteCount
+ mov sp, ax
+ };
+
+ /*
+ ** Resume execution of thread: t by restoring the thread's context.
+ **
+ */
+ Throw((_pr_currentThread)->md.context, 1);
+} /* --- end MD_RESTORE_CONTEXT() --- */
+
+
+static void TraceDispatch( PRThread *thread )
+{
+ int i;
+
+ /*
+ ** push all DispatchTrace objects to down one slot.
+ ** Note: the last entry is lost; last-1 becomes last, etc.
+ */
+ for( i = NUM_DISPATCHTRACE_OBJECTS -2; i >= 0; i-- )
+ {
+ dt[i +1] = dt[i];
+ }
+
+ /*
+ ** Build dt[0] from t
+ */
+ dt->thread = thread;
+ dt->state = thread->state;
+ dt->mdThreadNumber = thread->md.threadNumber;
+ dt->priority = thread->priority;
+
+ return;
+} /* --- end TraceDispatch() --- */
+
+
+/* $$ end W16thred.c */
diff --git a/pr/src/md/windows/w32poll.c b/pr/src/md/windows/w32poll.c
new file mode 100644
index 00000000..e71fd936
--- /dev/null
+++ b/pr/src/md/windows/w32poll.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * This file implements _PR_MD_PR_POLL for Win32.
+ */
+
+#include "primpl.h"
+
+#if !defined(_PR_GLOBAL_THREADS_ONLY)
+
+struct select_data_s {
+ PRInt32 status;
+ PRInt32 error;
+ fd_set *rd, *wt, *ex;
+ struct timeval *tv;
+};
+
+static void
+_PR_MD_select_thread(void *cdata)
+{
+ struct select_data_s *cd = (struct select_data_s *)cdata;
+
+ cd->status = select(0, cd->rd, cd->wt, cd->ex, cd->tv);
+
+ if (cd->status == SOCKET_ERROR) {
+ cd->error = WSAGetLastError();
+ }
+}
+
+#endif /* !defined(_PR_GLOBAL_THREADS_ONLY) */
+
+PRInt32
+_PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout)
+{
+ PRPollDesc *pd, *epd;
+ int n, err;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ fd_set rd, wt, ex;
+ struct timeval tv, *tvp = NULL;
+
+ FD_ZERO(&rd);
+ FD_ZERO(&wt);
+ FD_ZERO(&ex);
+
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ SOCKET osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if (in_flags & PR_POLL_READ) {
+ FD_SET(osfd, &rd);
+ }
+ if (in_flags & PR_POLL_WRITE) {
+ FD_SET(osfd, &wt);
+ }
+ if (in_flags & PR_POLL_EXCEPT) {
+ FD_SET(osfd, &ex);
+ }
+ }
+ if (timeout != PR_INTERVAL_NO_TIMEOUT) {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(timeout) % PR_USEC_PER_SEC;
+ tvp = &tv;
+ }
+
+#if defined(_PR_GLOBAL_THREADS_ONLY)
+ n = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+#else
+ if (_PR_IS_NATIVE_THREAD(me)) {
+ n = _MD_SELECT(0, &rd, &wt, &ex, tvp);
+ } else {
+ PRThread *selectThread;
+ struct select_data_s data;
+ data.status = 0;
+ data.error = 0;
+ data.rd = &rd;
+ data.wt = &wt;
+ data.ex = &ex;
+ data.tv = tvp;
+
+ selectThread = PR_CreateThread(PR_USER_THREAD,
+ _PR_MD_select_thread,
+ &data,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ if (selectThread == NULL) {
+ return -1;
+ }
+ PR_JoinThread(selectThread);
+ n = data.status;
+ if (n == SOCKET_ERROR) {
+ WSASetLastError(data.error);
+ }
+ }
+#endif
+
+ if (n > 0) {
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ SOCKET osfd;
+ PRInt16 in_flags = pd->in_flags;
+ PRInt16 out_flags = 0;
+ PRFileDesc *bottom = pd->fd;
+
+ if ((NULL == bottom) || (in_flags == 0)) {
+ pd->out_flags = 0;
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ osfd = (SOCKET) bottom->secret->md.osfd;
+
+ if ((in_flags & PR_POLL_READ) && FD_ISSET(osfd, &rd)) {
+ out_flags |= PR_POLL_READ;
+ }
+ if ((in_flags & PR_POLL_WRITE) && FD_ISSET(osfd, &wt)) {
+ out_flags |= PR_POLL_WRITE;
+ }
+ if ((in_flags & PR_POLL_EXCEPT) && FD_ISSET(osfd, &ex)) {
+ out_flags |= PR_POLL_EXCEPT;
+ }
+ pd->out_flags = out_flags;
+ if (out_flags) {
+ n++;
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else if (n == SOCKET_ERROR) {
+ err = WSAGetLastError();
+ if (err == WSAENOTSOCK) {
+ /* Find the bad fds */
+ n = 0;
+ for (pd = pds, epd = pd + npds; pd < epd; pd++) {
+ int optval;
+ int optlen = sizeof(optval);
+ PRFileDesc *bottom = pd->fd;
+
+ pd->out_flags = 0;
+ if ((NULL == bottom) || (pd->in_flags == 0)) {
+ continue;
+ }
+ while (bottom->lower != NULL) {
+ bottom = bottom->lower;
+ }
+ if (getsockopt(bottom->secret->md.osfd, SOL_SOCKET,
+ SO_TYPE, (char *) &optval, &optlen) == -1) {
+ PR_ASSERT(WSAGetLastError() == WSAENOTSOCK);
+ if (WSAGetLastError() == WSAENOTSOCK) {
+ pd->out_flags = PR_POLL_NVAL;
+ n++;
+ }
+ }
+ }
+ PR_ASSERT(n > 0);
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(err);
+ }
+ }
+
+ return n;
+}
diff --git a/pr/src/md/windows/w95cv.c b/pr/src/md/windows/w95cv.c
new file mode 100644
index 00000000..dfbcb05a
--- /dev/null
+++ b/pr/src/md/windows/w95cv.c
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/*
+ * w95cv.c -- Windows 95 Machine-Dependent Code for Condition Variables
+ *
+ * We implement our own condition variable wait queue. Each thread
+ * has a semaphore object (thread->md.blocked_sema) to block on while
+ * waiting on a condition variable.
+ *
+ * We use a deferred condition notify algorithm. When PR_NotifyCondVar
+ * or PR_NotifyAllCondVar is called, the condition notifies are simply
+ * recorded in the _MDLock structure. We defer the condition notifies
+ * until right after we unlock the lock. This way the awakened threads
+ * have a better chance to reaquire the lock.
+ */
+
+#include "primpl.h"
+
+/*
+ * AddThreadToCVWaitQueueInternal --
+ *
+ * Add the thread to the end of the condition variable's wait queue.
+ * The CV's lock must be locked when this function is called.
+ */
+
+static void
+AddThreadToCVWaitQueueInternal(PRThread *thred, struct _MDCVar *cv)
+{
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait += 1;
+ thred->md.inCVWaitQueue = PR_TRUE;
+ thred->md.next = NULL;
+ thred->md.prev = cv->waitTail;
+ if (cv->waitHead == NULL) {
+ cv->waitHead = thred;
+ } else {
+ cv->waitTail->md.next = thred;
+ }
+ cv->waitTail = thred;
+}
+
+/*
+ * md_UnlockAndPostNotifies --
+ *
+ * Unlock the lock, and then do the deferred condition notifies.
+ * If waitThred and waitCV are not NULL, waitThred is added to
+ * the wait queue of waitCV before the lock is unlocked.
+ *
+ * This function is called by _PR_MD_WAIT_CV and _PR_MD_UNLOCK,
+ * the two places where a lock is unlocked.
+ */
+static void
+md_UnlockAndPostNotifies(
+ _MDLock *lock,
+ PRThread *waitThred,
+ _MDCVar *waitCV)
+{
+ PRIntn index;
+ _MDNotified post;
+ _MDNotified *notified, *prev = NULL;
+
+ /*
+ * Time to actually notify any conditions that were affected
+ * while the lock was held. Get a copy of the list that's in
+ * the lock structure and then zero the original. If it's
+ * linked to other such structures, we own that storage.
+ */
+ post = lock->notified; /* a safe copy; we own the lock */
+
+#if defined(DEBUG)
+ ZeroMemory(&lock->notified, sizeof(_MDNotified)); /* reset */
+#else
+ lock->notified.length = 0; /* these are really sufficient */
+ lock->notified.link = NULL;
+#endif
+
+ /*
+ * Figure out how many threads we need to wake up.
+ */
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ _MDCVar *cv = notified->cv[index].cv;
+ PRThread *thred;
+ int i;
+
+ /* Fast special case: no waiting threads */
+ if (cv->waitHead == NULL) {
+ notified->cv[index].notifyHead = NULL;
+ continue;
+ }
+
+ /* General case */
+ if (-1 == notified->cv[index].times) {
+ /* broadcast */
+ thred = cv->waitHead;
+ while (thred != NULL) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = cv->waitTail = NULL;
+ cv->nwait = 0;
+ } else {
+ thred = cv->waitHead;
+ i = notified->cv[index].times;
+ while (thred != NULL && i > 0) {
+ thred->md.inCVWaitQueue = PR_FALSE;
+ thred = thred->md.next;
+ i--;
+ }
+ notified->cv[index].notifyHead = cv->waitHead;
+ cv->waitHead = thred;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ if (cv->waitHead->md.prev != NULL) {
+ cv->waitHead->md.prev->md.next = NULL;
+ cv->waitHead->md.prev = NULL;
+ }
+ }
+ cv->nwait -= notified->cv[index].times - i;
+ }
+ }
+ notified = notified->link;
+ } while (NULL != notified);
+
+ if (waitThred) {
+ AddThreadToCVWaitQueueInternal(waitThred, waitCV);
+ }
+
+ /* Release the lock before notifying */
+ LeaveCriticalSection(&lock->mutex);
+
+ notified = &post; /* this is where we start */
+ do {
+ for (index = 0; index < notified->length; ++index) {
+ PRThread *thred;
+ PRThread *next;
+
+ thred = notified->cv[index].notifyHead;
+ while (thred != NULL) {
+ BOOL rv;
+
+ next = thred->md.next;
+ thred->md.prev = thred->md.next = NULL;
+
+ rv = ReleaseSemaphore(thred->md.blocked_sema, 1, NULL);
+ PR_ASSERT(rv != 0);
+ thred = next;
+ }
+ }
+ prev = notified;
+ notified = notified->link;
+ if (&post != prev) PR_DELETE(prev);
+ } while (NULL != notified);
+}
+
+/*
+ * Notifies just get posted to the protecting mutex. The
+ * actual notification is done when the lock is released so that
+ * MP systems don't contend for a lock that they can't have.
+ */
+static void md_PostNotifyToCvar(_MDCVar *cvar, _MDLock *lock,
+ PRBool broadcast)
+{
+ PRIntn index = 0;
+ _MDNotified *notified = &lock->notified;
+
+ while (1) {
+ for (index = 0; index < notified->length; ++index) {
+ if (notified->cv[index].cv == cvar) {
+ if (broadcast) {
+ notified->cv[index].times = -1;
+ } else if (-1 != notified->cv[index].times) {
+ notified->cv[index].times += 1;
+ }
+ return;
+ }
+ }
+ /* if not full, enter new CV in this array */
+ if (notified->length < _MD_CV_NOTIFIED_LENGTH) break;
+
+ /* if there's no link, create an empty array and link it */
+ if (NULL == notified->link) {
+ notified->link = PR_NEWZAP(_MDNotified);
+ }
+
+ notified = notified->link;
+ }
+
+ /* A brand new entry in the array */
+ notified->cv[index].times = (broadcast) ? -1 : 1;
+ notified->cv[index].cv = cvar;
+ notified->length += 1;
+}
+
+/*
+ * _PR_MD_NEW_CV() -- Creating new condition variable
+ * ... Solaris uses cond_init() in similar function.
+ *
+ * returns: -1 on failure
+ * 0 when it succeeds.
+ *
+ */
+PRInt32
+_PR_MD_NEW_CV(_MDCVar *cv)
+{
+ cv->magic = _MD_MAGIC_CV;
+ /*
+ * The waitHead, waitTail, and nwait fields are zeroed
+ * when the PRCondVar structure is created.
+ */
+ return 0;
+}
+
+void _PR_MD_FREE_CV(_MDCVar *cv)
+{
+ cv->magic = (PRUint32)-1;
+ return;
+}
+
+/*
+ * _PR_MD_WAIT_CV() -- Wait on condition variable
+ */
+void _PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout )
+{
+ PRThread *thred = _PR_MD_CURRENT_THREAD();
+ DWORD rv;
+ DWORD msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(timeout);
+
+ /*
+ * If we have pending notifies, post them now.
+ */
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, thred, cv);
+ } else {
+ AddThreadToCVWaitQueueInternal(thred, cv);
+ LeaveCriticalSection(&lock->mutex);
+ }
+
+ /* Wait for notification or timeout; don't really care which */
+ rv = WaitForSingleObject(thred->md.blocked_sema, msecs);
+
+ EnterCriticalSection(&(lock->mutex));
+
+ PR_ASSERT(rv != WAIT_ABANDONED);
+ PR_ASSERT(rv != WAIT_FAILED);
+ PR_ASSERT(rv != WAIT_OBJECT_0 || thred->md.inCVWaitQueue == PR_FALSE);
+
+ if (rv == WAIT_TIMEOUT) {
+ if (thred->md.inCVWaitQueue) {
+ PR_ASSERT((cv->waitTail != NULL && cv->waitHead != NULL)
+ || (cv->waitTail == NULL && cv->waitHead == NULL));
+ cv->nwait -= 1;
+ thred->md.inCVWaitQueue = PR_FALSE;
+ if (cv->waitHead == thred) {
+ cv->waitHead = thred->md.next;
+ if (cv->waitHead == NULL) {
+ cv->waitTail = NULL;
+ } else {
+ cv->waitHead->md.prev = NULL;
+ }
+ } else {
+ PR_ASSERT(thred->md.prev != NULL);
+ thred->md.prev->md.next = thred->md.next;
+ if (thred->md.next != NULL) {
+ thred->md.next->md.prev = thred->md.prev;
+ } else {
+ PR_ASSERT(cv->waitTail == thred);
+ cv->waitTail = thred->md.prev;
+ }
+ }
+ thred->md.next = thred->md.prev = NULL;
+ } else {
+ /*
+ * This thread must have been notified, but the
+ * ReleaseSemaphore call happens after WaitForSingleObject
+ * times out. Wait on the semaphore again to make it
+ * non-signaled. We assume this wait won't take long.
+ */
+ rv = WaitForSingleObject(thred->md.blocked_sema, INFINITE);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE);
+ return;
+} /* --- end _PR_MD_WAIT_CV() --- */
+
+void _PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_FALSE);
+ return;
+}
+
+void _PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock)
+{
+ md_PostNotifyToCvar(cv, lock, PR_TRUE);
+ return;
+}
+
+void _PR_MD_UNLOCK(_MDLock *lock)
+{
+ if (0 != lock->notified.length) {
+ md_UnlockAndPostNotifies(lock, NULL, NULL);
+ } else {
+ LeaveCriticalSection(&lock->mutex);
+ }
+ return;
+}
diff --git a/pr/src/md/windows/w95io.c b/pr/src/md/windows/w95io.c
new file mode 100644
index 00000000..90811721
--- /dev/null
+++ b/pr/src/md/windows/w95io.c
@@ -0,0 +1,871 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Windows 95 IO module
+ *
+ * Assumes synchronous I/O.
+ *
+ */
+
+#include "primpl.h"
+#include <direct.h>
+
+struct _MDLock _pr_ioq_lock;
+
+/*
+ * The NSPR epoch (00:00:00 1 Jan 1970 UTC) in FILETIME.
+ * We store the value in a PRTime variable for convenience.
+ * This constant is used by _PR_FileTimeToPRTime().
+ */
+static const PRTime _pr_filetime_offset = 116444736000000000i64;
+
+void
+_PR_MD_INIT_IO()
+{
+ WORD WSAVersion = 0x0101;
+ WSADATA WSAData;
+
+ WSAStartup( WSAVersion, &WSAData );
+
+#ifdef DEBUG
+ /* Doublecheck _pr_filetime_offset's hard-coded value is correct. */
+ {
+ SYSTEMTIME systime;
+ union {
+ PRTime prt;
+ FILETIME ft;
+ } filetime;
+ BOOL rv;
+
+ systime.wYear = 1970;
+ systime.wMonth = 1;
+ /* wDayOfWeek is ignored */
+ systime.wDay = 1;
+ systime.wHour = 0;
+ systime.wMinute = 0;
+ systime.wSecond = 0;
+ systime.wMilliseconds = 0;
+
+ rv = SystemTimeToFileTime(&systime, &filetime.ft);
+ PR_ASSERT(0 != rv);
+ PR_ASSERT(filetime.prt == _pr_filetime_offset);
+ }
+#endif /* DEBUG */
+}
+
+PRStatus
+_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
+{
+ DWORD rv;
+
+ PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ?
+ INFINITE : PR_IntervalToMilliseconds(ticks);
+ rv = WaitForSingleObject(thread->md.blocked_sema, msecs);
+ switch(rv)
+ {
+ case WAIT_OBJECT_0:
+ return PR_SUCCESS;
+ break;
+ case WAIT_TIMEOUT:
+ _PR_THREAD_LOCK(thread);
+ if (thread->state == _PR_IO_WAIT) {
+ ;
+ } else {
+ if (thread->wait.cvar != NULL) {
+ thread->wait.cvar = NULL;
+ _PR_THREAD_UNLOCK(thread);
+ } else {
+ /* The CVAR was notified just as the timeout
+ * occurred. This led to us being notified twice.
+ * call WaitForSingleObject() to clear the semaphore.
+ */
+ _PR_THREAD_UNLOCK(thread);
+ rv = WaitForSingleObject(thread->md.blocked_sema, 0);
+ PR_ASSERT(rv == WAIT_OBJECT_0);
+ }
+ }
+ return PR_SUCCESS;
+ break;
+ default:
+ return PR_FAILURE;
+ break;
+ }
+}
+PRStatus
+_PR_MD_WAKEUP_WAITER(PRThread *thread)
+{
+ if ( _PR_IS_NATIVE_THREAD(thread) )
+ {
+ if (ReleaseSemaphore(thread->md.blocked_sema, 1, NULL) == FALSE)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+ }
+}
+
+
+/* --- FILE IO ----------------------------------------------------------- */
+/*
+ * _PR_MD_OPEN() -- Open a file
+ *
+ * returns: a fileHandle
+ *
+ * The NSPR open flags (osflags) are translated into flags for Win95
+ *
+ * Mode seems to be passed in as a unix style file permissions argument
+ * as in 0666, in the case of opening the logFile.
+ *
+ */
+PRInt32
+_PR_MD_OPEN(const char *name, PRIntn osflags, int mode)
+{
+ HANDLE file;
+ PRInt32 access = 0;
+ PRInt32 flags = 0;
+ PRInt32 flag6 = 0;
+
+ if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
+
+ if (osflags & PR_RDONLY || osflags & PR_RDWR)
+ access |= GENERIC_READ;
+ if (osflags & PR_WRONLY || osflags & PR_RDWR)
+ access |= GENERIC_WRITE;
+ if (osflags & PR_CREATE_FILE)
+ flags = OPEN_ALWAYS;
+ else if (osflags & PR_TRUNCATE)
+ flags = CREATE_ALWAYS;
+ else
+ flags = OPEN_EXISTING;
+
+ file = CreateFile(name,
+ access,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ flags,
+ flag6,
+ NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ _PR_MD_MAP_OPEN_ERROR(GetLastError());
+ return -1;
+ }
+
+ return (PRInt32)file;
+}
+
+PRInt32
+_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRUint32 bytes;
+ int rv, err;
+
+ rv = ReadFile((HANDLE)fd->secret->md.osfd,
+ (LPVOID)buf,
+ len,
+ &bytes,
+ NULL);
+
+ if (rv == 0)
+ {
+ err = GetLastError();
+ /* ERROR_HANDLE_EOF can only be returned by async io */
+ PR_ASSERT(err != ERROR_HANDLE_EOF);
+ if (err == ERROR_BROKEN_PIPE)
+ return 0;
+ else {
+ _PR_MD_MAP_READ_ERROR(err);
+ return -1;
+ }
+ }
+ return bytes;
+}
+
+PRInt32
+_PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ PRInt32 f = fd->secret->md.osfd;
+ PRInt32 bytes;
+ int rv;
+ PRThread *me = _PR_MD_CURRENT_THREAD();
+
+ rv = WriteFile((HANDLE)f,
+ buf,
+ len,
+ &bytes,
+ NULL );
+
+ if (rv == 0)
+ {
+ _PR_MD_MAP_WRITE_ERROR(GetLastError());
+ return -1;
+ }
+ return bytes;
+} /* --- end _PR_MD_WRITE() --- */
+
+PRInt32
+_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence)
+{
+ PRInt32 rv;
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, offset, 0, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv) {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ } else
+ return rv;
+}
+
+PRInt64
+_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence)
+{
+ PRInt64 result;
+ PRInt32 rv, low = (PRInt32)offset, hi = (PRInt32)(offset >> 32);
+
+ rv = SetFilePointer((HANDLE)fd->secret->md.osfd, low, &hi, whence);
+
+ /*
+ * If the lpDistanceToMoveHigh argument (third argument) is
+ * NULL, SetFilePointer returns 0xffffffff on failure.
+ */
+ if (-1 == rv)
+ {
+ _PR_MD_MAP_LSEEK_ERROR(GetLastError());
+ return -1;
+ }
+
+ result = (hi << 32) + rv;
+ return result;
+}
+
+/*
+ * This is documented to succeed on read-only files, but Win32's
+ * FlushFileBuffers functions fails with "access denied" in such a
+ * case. So we only signal an error if the error is *not* "access
+ * denied".
+ */
+PRInt32
+_PR_MD_FSYNC(PRFileDesc *fd)
+{
+ /*
+ * From the documentation:
+ *
+ * On Windows NT, the function FlushFileBuffers fails if hFile
+ * is a handle to console output. That is because console
+ * output is not buffered. The function returns FALSE, and
+ * GetLastError returns ERROR_INVALID_HANDLE.
+ *
+ * On the other hand, on Win95, it returns without error. I cannot
+ * assume that 0, 1, and 2 are console, because if someone closes
+ * System.out and then opens a file, they might get file descriptor
+ * 1. An error on *that* version of 1 should be reported, whereas
+ * an error on System.out (which was the original 1) should be
+ * ignored. So I use isatty() to ensure that such an error was due
+ * to this bogosity, and if it was, I ignore the error.
+ */
+
+ long handle = _get_osfhandle(fd->secret->md.osfd);
+ BOOL ok = FlushFileBuffers((HANDLE)handle);
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err != ERROR_ACCESS_DENIED) { // from winerror.h
+ _PR_MD_MAP_FSYNC_ERROR(err);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+PRInt32
+_MD_CloseFile(PRInt32 osfd)
+{
+ PRInt32 rv;
+
+ rv = (CloseHandle((HANDLE)osfd))?0:-1;
+ if (rv == -1)
+ _PR_MD_MAP_CLOSE_ERROR(GetLastError());
+ return rv;
+}
+
+
+/* --- DIR IO ------------------------------------------------------------ */
+#define GetFileFromDIR(d) (d)->d_entry.cFileName
+#define FileIsHidden(d) ((d)->d_entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
+
+void FlipSlashes(char *cp, int len)
+{
+ while (--len >= 0) {
+ if (cp[0] == '/') {
+ cp[0] = PR_DIRECTORY_SEPARATOR;
+ }
+ cp++;
+ }
+}
+
+/*
+**
+** Local implementations of standard Unix RTL functions which are not provided
+** by the VC RTL.
+**
+*/
+
+PRStatus
+_PR_MD_CLOSE_DIR(_MDDir *d)
+{
+ if ( d ) {
+ if (FindClose(d->d_hdl)) {
+ d->magic = (PRUint32)-1;
+ return PR_SUCCESS;
+ } else {
+ _PR_MD_MAP_CLOSEDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return PR_FAILURE;
+}
+
+
+PRStatus
+_PR_MD_OPEN_DIR(_MDDir *d, const char *name)
+{
+ char filename[ MAX_PATH ];
+
+ PR_snprintf(filename, MAX_PATH, "%s%s%s",
+ name, PR_DIRECTORY_SEPARATOR_STR, "*.*");
+ FlipSlashes( filename, strlen(filename) );
+
+ d->d_hdl = FindFirstFile( filename, &(d->d_entry) );
+ if ( d->d_hdl == INVALID_HANDLE_VALUE ) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return PR_FAILURE;
+ }
+ d->firstEntry = PR_TRUE;
+ d->magic = _MD_MAGIC_DIR;
+ return PR_SUCCESS;
+}
+
+char *
+_PR_MD_READ_DIR(_MDDir *d, PRIntn flags)
+{
+ PRInt32 err;
+ BOOL rv;
+ char *fileName;
+
+ if ( d ) {
+ while (1) {
+ if (d->firstEntry) {
+ d->firstEntry = PR_FALSE;
+ rv = 1;
+ } else {
+ rv = FindNextFile(d->d_hdl, &(d->d_entry));
+ }
+ if (rv == 0) {
+ break;
+ }
+ fileName = GetFileFromDIR(d);
+ if ( (flags & PR_SKIP_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_DOT_DOT) &&
+ (fileName[0] == '.') && (fileName[1] == '.') &&
+ (fileName[2] == '\0'))
+ continue;
+ if ( (flags & PR_SKIP_HIDDEN) && FileIsHidden(d))
+ continue;
+ return fileName;
+ }
+ err = GetLastError();
+ PR_ASSERT(NO_ERROR != err);
+ _PR_MD_MAP_READDIR_ERROR(err);
+ return NULL;
+ }
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return NULL;
+}
+
+PRInt32
+_PR_MD_DELETE(const char *name)
+{
+ if (DeleteFile(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_DELETE_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+static void
+_PR_FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
+{
+ PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
+ CopyMemory(prtm, filetime, sizeof(PRTime));
+ *prtm = (*prtm - _pr_filetime_offset) / 10i64;
+
+#ifdef DEBUG
+ /* Doublecheck our calculation. */
+ {
+ SYSTEMTIME systime;
+ PRExplodedTime etm;
+ PRTime cmp; /* for comparison */
+ BOOL rv;
+
+ rv = FileTimeToSystemTime(filetime, &systime);
+ PR_ASSERT(0 != rv);
+
+ /*
+ * PR_ImplodeTime ignores wday and yday.
+ */
+ etm.tm_usec = systime.wMilliseconds * PR_USEC_PER_MSEC;
+ etm.tm_sec = systime.wSecond;
+ etm.tm_min = systime.wMinute;
+ etm.tm_hour = systime.wHour;
+ etm.tm_mday = systime.wDay;
+ etm.tm_month = systime.wMonth - 1;
+ etm.tm_year = systime.wYear;
+ /*
+ * It is not well-documented what time zone the FILETIME's
+ * are in. WIN32_FIND_DATA is documented to be in UTC (GMT).
+ * But BY_HANDLE_FILE_INFORMATION is unclear about this.
+ * By our best judgement, we assume that FILETIME is in UTC.
+ */
+ etm.tm_params.tp_gmt_offset = 0;
+ etm.tm_params.tp_dst_offset = 0;
+ cmp = PR_ImplodeTime(&etm);
+
+ /*
+ * SYSTEMTIME is in milliseconds precision, so we convert PRTime's
+ * microseconds to milliseconds before doing the comparison.
+ */
+ PR_ASSERT((cmp / PR_USEC_PER_MSEC) == (*prtm / PR_USEC_PER_MSEC));
+ }
+#endif /* DEBUG */
+}
+
+PRInt32
+_PR_MD_STAT(const char *fn, struct stat *info)
+{
+ PRInt32 rv;
+
+ rv = _stat(fn, (struct _stat *)info);
+ if (-1 == rv) {
+ /*
+ * Check for MSVC runtime library _stat() bug.
+ * (It's really a bug in FindFirstFile().)
+ * If a pathname ends in a backslash or slash,
+ * e.g., c:\temp\ or c:/temp/, _stat() will fail.
+ * Note: a pathname ending in a slash (e.g., c:/temp/)
+ * can be handled by _stat() on NT but not on Win95.
+ *
+ * We remove the backslash or slash at the end and
+ * try again.
+ */
+
+ int len = strlen(fn);
+ if (len > 0 && len <= _MAX_PATH
+ && (fn[len - 1] == '\\' || fn[len - 1] == '/')) {
+ char newfn[_MAX_PATH + 1];
+
+ strcpy(newfn, fn);
+ newfn[len - 1] = '\0';
+ rv = _stat(newfn, (struct _stat *)info);
+ }
+ }
+
+ if (-1 == rv) {
+ _PR_MD_MAP_STAT_ERROR(errno);
+ }
+ return rv;
+}
+
+#define _PR_IS_SLASH(ch) ((ch) == '/' || (ch) == '\\')
+
+/*
+ * IsRootDirectory --
+ *
+ * Return PR_TRUE if the pathname 'fn' is a valid root directory,
+ * else return PR_FALSE. The char buffer pointed to by 'fn' must
+ * be writable. During the execution of this function, the contents
+ * of the buffer pointed to by 'fn' may be modified, but on return
+ * the original contents will be restored. 'buflen' is the size of
+ * the buffer pointed to by 'fn'.
+ *
+ * Root directories come in three formats:
+ * 1. / or \, meaning the root directory of the current drive.
+ * 2. C:/ or C:\, where C is a drive letter.
+ * 3. \\<server name>\<share point name>\ or
+ * \\<server name>\<share point name>, meaning the root directory
+ * of a UNC (Universal Naming Convention) name.
+ */
+
+static PRBool
+IsRootDirectory(char *fn, size_t buflen)
+{
+ char *p;
+ PRBool slashAdded = PR_FALSE;
+ PRBool rv = PR_FALSE;
+
+ if (_PR_IS_SLASH(fn[0]) && fn[1] == '\0') {
+ return PR_TRUE;
+ }
+
+ if (isalpha(fn[0]) && fn[1] == ':' && _PR_IS_SLASH(fn[2])
+ && fn[3] == '\0') {
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ return rv;
+ }
+
+ /* The UNC root directory */
+
+ if (_PR_IS_SLASH(fn[0]) && _PR_IS_SLASH(fn[1])) {
+ /* The 'server' part should have at least one character. */
+ p = &fn[2];
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the next slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (*p == '\0') {
+ return PR_FALSE;
+ }
+
+ /* The 'share' part should have at least one character. */
+ p++;
+ if (*p == '\0' || _PR_IS_SLASH(*p)) {
+ return PR_FALSE;
+ }
+
+ /* look for the final slash */
+ do {
+ p++;
+ } while (*p != '\0' && !_PR_IS_SLASH(*p));
+ if (_PR_IS_SLASH(*p) && p[1] != '\0') {
+ return PR_FALSE;
+ }
+ if (*p == '\0') {
+ /*
+ * GetDriveType() doesn't work correctly if the
+ * path is of the form \\server\share, so we add
+ * a final slash temporarily.
+ */
+ if ((p + 1) < (fn + buflen)) {
+ *p++ = '\\';
+ *p = '\0';
+ slashAdded = PR_TRUE;
+ } else {
+ return PR_FALSE; /* name too long */
+ }
+ }
+ rv = GetDriveType(fn) > 1 ? PR_TRUE : PR_FALSE;
+ /* restore the 'fn' buffer */
+ if (slashAdded) {
+ *--p = '\0';
+ }
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO64(const char *fn, PRFileInfo64 *info)
+{
+ HANDLE hFindFile;
+ WIN32_FIND_DATA findFileData;
+ char pathbuf[MAX_PATH + 1];
+
+ if (NULL == fn || '\0' == *fn) {
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+
+ /*
+ * FindFirstFile() expands wildcard characters. So
+ * we make sure the pathname contains no wildcard.
+ */
+ if (NULL != strpbrk(fn, "?*")) {
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, 0);
+ return -1;
+ }
+
+ hFindFile = FindFirstFile(fn, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ DWORD len;
+ char *filePart;
+
+ /*
+ * FindFirstFile() does not work correctly on root directories.
+ * It also doesn't work correctly on a pathname that ends in a
+ * slash. So we first check to see if the pathname specifies a
+ * root directory. If not, and if the pathname ends in a slash,
+ * we remove the final slash and try again.
+ */
+
+ /*
+ * If the pathname does not contain ., \, and /, it cannot be
+ * a root directory or a pathname that ends in a slash.
+ */
+ if (NULL == strpbrk(fn, ".\\/")) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ len = GetFullPathName(fn, sizeof(pathbuf), pathbuf,
+ &filePart);
+ PR_ASSERT(0 != len);
+ if (len > sizeof(pathbuf)) {
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, 0);
+ return -1;
+ }
+ if (IsRootDirectory(pathbuf, sizeof(pathbuf))) {
+ info->type = PR_FILE_DIRECTORY;
+ info->size = 0;
+ /*
+ * These timestamps don't make sense for root directories.
+ */
+ info->modifyTime = 0;
+ info->creationTime = 0;
+ return 0;
+ }
+ if (!_PR_IS_SLASH(pathbuf[len - 1])) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ } else {
+ pathbuf[len - 1] = '\0';
+ hFindFile = FindFirstFile(pathbuf, &findFileData);
+ if (INVALID_HANDLE_VALUE == hFindFile) {
+ _PR_MD_MAP_OPENDIR_ERROR(GetLastError());
+ return -1;
+ }
+ }
+ }
+
+ FindClose(hFindFile);
+
+ if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ info->type = PR_FILE_DIRECTORY;
+ } else {
+ info->type = PR_FILE_FILE;
+ }
+
+ info->size = findFileData.nFileSizeHigh;
+ info->size = (info->size << 32) + findFileData.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&findFileData.ftLastWriteTime, &info->modifyTime);
+
+ if (0 == findFileData.ftCreationTime.dwLowDateTime &&
+ 0 == findFileData.ftCreationTime.dwHighDateTime) {
+ info->creationTime = info->modifyTime;
+ } else {
+ _PR_FileTimeToPRTime(&findFileData.ftCreationTime,
+ &info->creationTime);
+ }
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ PRInt32 rv = _PR_MD_GETFILEINFO64(fn, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->size = (PRUint32) info64.size;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO64(const PRFileDesc *fd, PRFileInfo64 *info)
+{
+ int rv;
+
+ BY_HANDLE_FILE_INFORMATION hinfo;
+
+ rv = GetFileInformationByHandle((HANDLE)fd->secret->md.osfd, &hinfo);
+ if (rv == FALSE) {
+ _PR_MD_MAP_FSTAT_ERROR(GetLastError());
+ return -1;
+ }
+
+ if (hinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ info->type = PR_FILE_DIRECTORY;
+ else
+ info->type = PR_FILE_FILE;
+
+ info->size = hinfo.nFileSizeHigh;
+ info->size = (info->size << 32) + hinfo.nFileSizeLow;
+
+ _PR_FileTimeToPRTime(&hinfo.ftLastWriteTime, &(info->modifyTime) );
+ _PR_FileTimeToPRTime(&hinfo.ftCreationTime, &(info->creationTime) );
+
+ return 0;
+}
+
+PRInt32
+_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info)
+{
+ PRFileInfo64 info64;
+ int rv = _PR_MD_GETOPENFILEINFO64(fd, &info64);
+ if (0 == rv)
+ {
+ info->type = info64.type;
+ info->modifyTime = info64.modifyTime;
+ info->creationTime = info64.creationTime;
+ LL_L2I(info->size, info64.size);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_RENAME(const char *from, const char *to)
+{
+ /* Does this work with dot-relative pathnames? */
+ if (MoveFile(from, to)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RENAME_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_ACCESS(const char *name, PRIntn how)
+{
+PRInt32 rv;
+ switch (how) {
+ case PR_ACCESS_WRITE_OK:
+ rv = _access(name, 02);
+ break;
+ case PR_ACCESS_READ_OK:
+ rv = _access(name, 04);
+ break;
+ case PR_ACCESS_EXISTS:
+ return _access(name, 00);
+ break;
+ default:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
+ return -1;
+ }
+ if (rv < 0)
+ _PR_MD_MAP_ACCESS_ERROR(errno);
+ return rv;
+}
+
+PRInt32
+_PR_MD_MKDIR(const char *name, PRIntn mode)
+{
+ /* XXXMB - how to translate the "mode"??? */
+ if (CreateDirectory(name, NULL)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_MKDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRInt32
+_PR_MD_RMDIR(const char *name)
+{
+ if (RemoveDirectory(name)) {
+ return 0;
+ } else {
+ _PR_MD_MAP_RMDIR_ERROR(GetLastError());
+ return -1;
+ }
+}
+
+PRStatus
+_PR_MD_LOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ /*
+ * loop trying to LockFile(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+
+ rv = LockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+ if ( rv == FALSE )
+ {
+ DWORD rc = GetLastError();
+ Sleep( 50 ); // Sleep() a few milisecs and try again.
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_LOCKFILE() */
+
+PRStatus
+_PR_MD_TLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ /*
+ * loop trying to LockFile(),
+ * pause for a few miliseconds when can't get the lock
+ * and try again
+ */
+ for( rv = FALSE; rv == FALSE; /* do nothing */ )
+ {
+
+ rv = LockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+ if ( rv == FALSE )
+ {
+ DWORD rc = GetLastError();
+ Sleep( 50 ); // Sleep() a few milisecs and try again.
+ }
+ } /* end for() */
+ return PR_SUCCESS;
+} /* end _PR_MD_TLOCKFILE() */
+
+
+PRStatus
+_PR_MD_UNLOCKFILE(PRInt32 f)
+{
+ PRInt32 rv;
+
+ rv = UnlockFile( (HANDLE) f,
+ 0l, 0l,
+ 0x7fffffff, 0xffffffff );
+
+ if ( rv )
+ {
+ return PR_SUCCESS;
+ }
+ else
+ {
+ int err = GetLastError();
+ return PR_FAILURE;
+ }
+} /* end _PR_MD_UNLOCKFILE() */
+
diff --git a/pr/src/md/windows/w95sock.c b/pr/src/md/windows/w95sock.c
new file mode 100644
index 00000000..c5fbea4c
--- /dev/null
+++ b/pr/src/md/windows/w95sock.c
@@ -0,0 +1,614 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+/* Win95 Sockets module
+ *
+ */
+
+#include "primpl.h"
+
+
+/* --- SOCKET IO --------------------------------------------------------- */
+
+
+PRInt32
+_PR_MD_SOCKET(int af, int type, int flags)
+{
+ SOCKET sock;
+ PRUint32 one = 1;
+ PRInt32 rv;
+ PRInt32 err;
+
+ sock = socket(af, type, flags);
+
+ if (sock == INVALID_SOCKET )
+ {
+ int rv = WSAGetLastError();
+ closesocket(sock);
+ _PR_MD_MAP_SOCKET_ERROR(rv);
+ return (PRInt32)INVALID_SOCKET;
+ }
+
+ /*
+ ** Make the socket Non-Blocking
+ */
+ rv = ioctlsocket( sock, FIONBIO, &one);
+ if ( rv != 0 )
+ {
+ err = WSAGetLastError();
+ return -1;
+ }
+
+ return (PRInt32)sock;
+}
+
+/*
+** _MD_CloseSocket() -- Close a socket
+**
+*/
+PRInt32
+_MD_CloseSocket(PRInt32 osfd)
+{
+ PRInt32 rv = SOCKET_ERROR;
+
+ rv = closesocket((SOCKET) osfd );
+ if (rv < 0)
+ _PR_MD_MAP_SOCKET_ERROR(WSAGetLastError());
+
+ return rv;
+}
+
+PRInt32
+_MD_SocketAvailable(PRFileDesc *fd)
+{
+ PRInt32 result;
+
+ if (ioctlsocket(fd->secret->md.osfd, FIONREAD, &result) < 0) {
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, WSAGetLastError());
+ return -1;
+ }
+ return result;
+}
+
+PRInt32
+_MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen,
+ PRIntervalTime timeout )
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ fd_set rd;
+ struct timeval tv, *tvp;
+
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ while ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ }
+ else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+ }
+ else if (timeout == PR_INTERVAL_NO_WAIT)
+ {
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ }
+ else
+ {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ return(rv);
+ }
+ else
+ {
+retry:
+ if ((rv = accept(osfd, (struct sockaddr *) raddr, rlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+
+ rv = select(osfd + 1, &rd, NULL, NULL, tvp);
+ if (rv > 0) {
+ goto retry;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ } else {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ }
+ } else {
+ _PR_MD_MAP_ACCEPT_ERROR(err);
+ }
+ }
+ }
+ return(rv);
+} /* end _MD_Accept() */
+
+
+PRInt32
+_PR_MD_CONNECT(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv;
+ int err, len;
+ fd_set wd, ex;
+ struct timeval tv, *tvp;
+
+ if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1)
+ {
+ err = WSAGetLastError();
+ if ((!fd->secret->nonblocking) && (err == WSAEWOULDBLOCK))
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ tvp = NULL;
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ FD_ZERO(&ex);
+ FD_SET((SOCKET)osfd, &ex);
+ rv = select(osfd + 1, NULL, &wd, &ex, tvp);
+ if (rv > 0)
+ {
+ if (FD_ISSET((SOCKET)osfd, &ex))
+ {
+ Sleep(0);
+ len = sizeof(err);
+ if (getsockopt(osfd, SOL_SOCKET, SO_ERROR,
+ (char *) &err, &len) == SOCKET_ERROR)
+ {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ if (err != 0)
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ else
+ PR_SetError(PR_UNKNOWN_ERROR, 0);
+ return -1;
+ }
+ if (FD_ISSET((SOCKET)osfd, &wd))
+ {
+ /* it's connected */
+ return 0;
+ }
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return(-1);
+ } else if (rv < 0)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return(-1);
+ }
+ }
+ _PR_MD_MAP_CONNECT_ERROR(err);
+ }
+ return rv;
+}
+
+PRInt32
+_PR_MD_BIND(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen)
+{
+ PRInt32 rv;
+ int one = 1;
+
+ rv = bind(fd->secret->md.osfd, (const struct sockaddr *)&(addr->inet), addrlen);
+
+ if (rv == SOCKET_ERROR) {
+ _PR_MD_MAP_BIND_ERROR(WSAGetLastError());
+ return -1;
+ }
+
+ return 0;
+}
+
+
+PRInt32
+_PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recv( osfd, buf, amount, 0)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ }
+ else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECV_ERROR(err);
+ break;
+ }
+ } /* end while() */
+ return(rv);
+}
+
+PRInt32
+_PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount )
+ {
+ while ((rv = send( osfd, buf, amount, 0 )) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SEND_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_SENDTO(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags,
+ const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set wd;
+ PRInt32 bytesSent = 0;
+
+ while(bytesSent < amount)
+ {
+ while ((rv = sendto( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select(osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ else {
+ _PR_MD_MAP_SENDTO_ERROR(err);
+ return -1;
+ }
+ }
+ bytesSent += rv;
+ if (fd->secret->nonblocking)
+ {
+ break;
+ }
+ if ((rv >= 0) && (bytesSent < amount ))
+ {
+ if ( timeout == PR_INTERVAL_NO_TIMEOUT )
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&wd);
+ FD_SET((SOCKET)osfd, &wd);
+ if ((rv = select( osfd + 1, NULL, &wd, NULL, tvp)) == -1) {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ break;
+ }
+ if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ return -1;
+ }
+ }
+ }
+ return bytesSent;
+}
+
+PRInt32
+_PR_MD_RECVFROM(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags,
+ PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout)
+{
+ PRInt32 osfd = fd->secret->md.osfd;
+ PRInt32 rv, err;
+ struct timeval tv, *tvp;
+ fd_set rd;
+
+ while ((rv = recvfrom( osfd, buf, amount, 0, (struct sockaddr *) addr,
+ addrlen)) == -1)
+ {
+ if (((err = WSAGetLastError()) == WSAEWOULDBLOCK)
+ && (!fd->secret->nonblocking))
+ {
+ if (timeout == PR_INTERVAL_NO_TIMEOUT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = PR_IntervalToSeconds(timeout);
+ tv.tv_usec = PR_IntervalToMicroseconds(
+ timeout - PR_SecondsToInterval(tv.tv_sec));
+ tvp = &tv;
+ }
+ FD_ZERO(&rd);
+ FD_SET((SOCKET)osfd, &rd);
+ if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1)
+ {
+ _PR_MD_MAP_SELECT_ERROR(WSAGetLastError());
+ return -1;
+ } else if (rv == 0)
+ {
+ PR_SetError(PR_IO_TIMEOUT_ERROR, 0);
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+ _PR_MD_MAP_RECVFROM_ERROR(err);
+ break;
+ }
+ }
+ return(rv);
+}
+
+PRInt32
+_PR_MD_WRITEV(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout)
+{
+ int index;
+ int sent = 0;
+ int rv;
+
+ for (index=0; index < iov_size; index++)
+ {
+ rv = _PR_MD_SEND(fd, iov[index].iov_base, iov[index].iov_len, 0, timeout);
+ if (rv > 0)
+ sent += rv;
+ if ( rv != iov[index].iov_len )
+ {
+ if (rv < 0)
+ {
+ if (fd->secret->nonblocking
+ && (PR_GetError() == PR_WOULD_BLOCK_ERROR)
+ && (sent > 0))
+ {
+ return sent;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ /* Only a nonblocking socket can have partial sends */
+ PR_ASSERT(fd->secret->nonblocking);
+ return sent;
+ }
+ }
+ return sent;
+}
+
+PRInt32
+_PR_MD_SHUTDOWN(PRFileDesc *fd, PRIntn how)
+{
+PRInt32 rv;
+
+ rv = shutdown(fd->secret->md.osfd, how);
+ if (rv < 0)
+ _PR_MD_MAP_SHUTDOWN_ERROR(WSAGetLastError());
+ return rv;
+}
+
+PRStatus
+_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getsockname((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len)
+{
+ PRInt32 rv;
+
+ rv = getpeername((SOCKET)fd->secret->md.osfd, (struct sockaddr *)addr, len);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETPEERNAME_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen)
+{
+ PRInt32 rv;
+
+ rv = getsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+PRStatus
+_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen)
+{
+ PRInt32 rv;
+
+ rv = setsockopt((SOCKET)fd->secret->md.osfd, level, optname, optval, optlen);
+ if (rv==0)
+ return PR_SUCCESS;
+ else {
+ _PR_MD_MAP_SETSOCKOPT_ERROR(WSAGetLastError());
+ return PR_FAILURE;
+ }
+}
+
+void
+_MD_MakeNonblock(PRFileDesc *f)
+{
+ return; // do nothing!
+}
diff --git a/pr/src/md/windows/w95thred.c b/pr/src/md/windows/w95thred.c
new file mode 100644
index 00000000..0b333b85
--- /dev/null
+++ b/pr/src/md/windows/w95thred.c
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "primpl.h"
+#include <process.h> /* for _beginthreadex() */
+
+extern void _PR_Win32InitTimeZone(void); /* defined in ntmisc.c */
+
+/* --- globals ------------------------------------------------ */
+__declspec(thread) struct PRThread *_pr_thread_last_run;
+__declspec(thread) struct PRThread *_pr_currentThread;
+__declspec(thread) struct _PRCPU *_pr_currentCPU;
+int _pr_intsOff = 0;
+_PRInterruptTable _pr_interruptTable[] = { { 0 } };
+
+void
+_PR_MD_EARLY_INIT()
+{
+ _PR_Win32InitTimeZone();
+}
+
+void _PR_MD_CLEANUP_BEFORE_EXIT(void)
+{
+ WSACleanup();
+}
+
+void
+_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread)
+{
+ /*
+ ** Warning:
+ ** --------
+ ** NSPR requires a real handle to every thread. GetCurrentThread()
+ ** returns a pseudo-handle which is not suitable for some thread
+ ** operations (ie. suspending). Therefore, get a real handle from
+ ** the pseudo handle via DuplicateHandle(...)
+ */
+ DuplicateHandle( GetCurrentProcess(), /* Process of source handle */
+ GetCurrentThread(), /* Pseudo Handle to dup */
+ GetCurrentProcess(), /* Process of handle */
+ &(thread->md.handle), /* resulting handle */
+ 0L, /* access flags */
+ FALSE, /* Inheritable */
+ DUPLICATE_SAME_ACCESS ); /* Options */
+}
+
+PRStatus
+_PR_MD_INIT_THREAD(PRThread *thread)
+{
+ if (thread->flags & _PR_PRIMORDIAL)
+ _PR_MD_INIT_PRIMORDIAL_THREAD(thread);
+
+ /* Create the blocking IO semaphore */
+ thread->md.blocked_sema = CreateSemaphore(NULL, 0, 1, NULL);
+ if (thread->md.blocked_sema == NULL)
+ return PR_FAILURE;
+ else
+ return PR_SUCCESS;
+}
+
+PRStatus
+_PR_MD_CREATE_THREAD(PRThread *thread,
+ void (*start)(void *),
+ PRThreadPriority priority,
+ PRThreadScope scope,
+ PRThreadState state,
+ PRUint32 stackSize)
+{
+
+ thread->md.handle = (HANDLE) _beginthreadex(
+ NULL,
+ thread->stack->stackSize,
+ (unsigned (__stdcall *)(void *))start,
+ (void *)thread,
+ CREATE_SUSPENDED,
+ &(thread->id));
+ if(!thread->md.handle) {
+ return PR_FAILURE;
+ }
+
+ thread->md.id = thread->id;
+ _PR_MD_SET_PRIORITY(&(thread->md), priority);
+
+ /* Activate the thread */
+ if ( ResumeThread( thread->md.handle ) != -1)
+ return PR_SUCCESS;
+
+ return PR_FAILURE;
+}
+
+void
+_PR_MD_YIELD(void)
+{
+ /* Can NT really yield at all? */
+ Sleep(0);
+}
+
+void
+_PR_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri)
+{
+ int nativePri;
+ BOOL rv;
+
+ if (newPri < PR_PRIORITY_FIRST) {
+ newPri = PR_PRIORITY_FIRST;
+ } else if (newPri > PR_PRIORITY_LAST) {
+ newPri = PR_PRIORITY_LAST;
+ }
+ switch (newPri) {
+ case PR_PRIORITY_LOW:
+ nativePri = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case PR_PRIORITY_NORMAL:
+ nativePri = THREAD_PRIORITY_NORMAL;
+ break;
+ case PR_PRIORITY_HIGH:
+ nativePri = THREAD_PRIORITY_ABOVE_NORMAL;
+ break;
+ case PR_PRIORITY_URGENT:
+ nativePri = THREAD_PRIORITY_HIGHEST;
+ }
+ rv = SetThreadPriority(thread->handle, nativePri);
+ PR_ASSERT(rv);
+ if (!rv) {
+ PR_LOG(_pr_thread_lm, PR_LOG_MIN,
+ ("PR_SetThreadPriority: can't set thread priority\n"));
+ }
+ return;
+}
+
+void
+_PR_MD_CLEAN_THREAD(PRThread *thread)
+{
+ if (thread->md.blocked_sema) {
+ CloseHandle(thread->md.blocked_sema);
+ thread->md.blocked_sema = 0;
+ }
+
+ if (thread->md.handle) {
+ CloseHandle(thread->md.handle);
+ thread->md.handle = 0;
+ }
+}
+
+void
+_PR_MD_EXIT_THREAD(PRThread *thread)
+{
+ _PR_MD_CLEAN_THREAD(thread);
+ _PR_MD_SET_CURRENT_THREAD(NULL);
+}
+
+
+void
+_PR_MD_EXIT(PRIntn status)
+{
+ _exit(status);
+}
+
+PRInt32 _PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask )
+{
+ int rv;
+
+ rv = SetThreadAffinityMask(thread->md.handle, mask);
+
+ return rv?0:-1;
+}
+
+PRInt32 _PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask)
+{
+ PRInt32 rv, system_mask;
+
+ rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask);
+
+ return rv?0:-1;
+}
+
+void
+_PR_MD_SUSPEND_CPU(_PRCPU *cpu)
+{
+ _PR_MD_SUSPEND_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_RESUME_CPU(_PRCPU *cpu)
+{
+ _PR_MD_RESUME_THREAD(cpu->thread);
+}
+
+void
+_PR_MD_SUSPEND_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ /* XXXMB - SuspendThread() is not a blocking call; how do we
+ * know when the thread is *REALLY* suspended?
+ */
+ previousSuspendCount = SuspendThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 0);
+ }
+}
+
+void
+_PR_MD_RESUME_THREAD(PRThread *thread)
+{
+ if (_PR_IS_NATIVE_THREAD(thread)) {
+ DWORD previousSuspendCount;
+ previousSuspendCount = ResumeThread(thread->md.handle);
+ PR_ASSERT(previousSuspendCount == 1);
+ }
+}
+
diff --git a/pr/src/md/windows/win32_errors.c b/pr/src/md/windows/win32_errors.c
new file mode 100644
index 00000000..df271678
--- /dev/null
+++ b/pr/src/md/windows/win32_errors.c
@@ -0,0 +1,1104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * The contents of this file are subject to the Netscape Public License
+ * Version 1.0 (the "NPL"); you may not use this file except in
+ * compliance with the NPL. You may obtain a copy of the NPL at
+ * http://www.mozilla.org/NPL/
+ *
+ * Software distributed under the NPL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
+ * for the specific language governing rights and limitations under the
+ * NPL.
+ *
+ * The Initial Developer of this code under the NPL is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1998 Netscape Communications Corporation. All Rights
+ * Reserved.
+ */
+
+#include "prerror.h"
+#include <errno.h>
+
+void _MD_win32_map_opendir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_OPEN_FILES:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_closedir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_unix_readdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_NO_MORE_FILES:
+ PR_SetError(PR_NO_MORE_FILES_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_delete_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for stat() is in errno. */
+void _MD_win32_map_stat_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_win32_map_fstat_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_rename_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+/* The error code for access() is in errno. */
+void _MD_win32_map_access_error(PRInt32 err)
+{
+ switch (err) {
+ case ENOENT:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case EACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ }
+}
+
+void _MD_win32_map_mkdir_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_rmdir_error(PRInt32 err)
+{
+
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_read_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_transmitfile_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_write_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_STACK_OVERFLOW:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_lseek_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_SEEK_ON_DEVICE:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_fsync_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_close_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_NOT_READY:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_socket_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEPROTONOSUPPORT:
+ PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_recv_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_recvfrom_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_send_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_sendto_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEMSGSIZE:
+ case WSAEINVAL:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NETNAME_DELETED:
+ PR_SetError(PR_CONNECT_RESET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_accept_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_WOULD_BLOCK_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEMFILE:
+ PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_acceptex_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ case ERROR_INVALID_USER_BUFFER:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_connect_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEWOULDBLOCK:
+ PR_SetError(PR_IN_PROGRESS_ERROR, err);
+ break;
+ case WSAEALREADY:
+ case WSAEINVAL:
+ PR_SetError(PR_ALREADY_INITIATED_ERROR, err);
+ break;
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEAFNOSUPPORT:
+ PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err);
+ break;
+ case WSAEISCONN:
+ PR_SetError(PR_IS_CONNECTED_ERROR, err);
+ break;
+ case WSAETIMEDOUT:
+ PR_SetError(PR_IO_TIMEOUT_ERROR, err);
+ break;
+ case WSAECONNREFUSED:
+ PR_SetError(PR_CONNECT_REFUSED_ERROR, err);
+ break;
+ case WSAENETUNREACH:
+ PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_bind_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEADDRNOTAVAIL:
+ PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err);
+ break;
+ case WSAEADDRINUSE:
+ PR_SetError(PR_ADDRESS_IN_USE_ERROR, err);
+ break;
+ case WSAEACCES:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_listen_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEOPNOTSUPP:
+ PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_shutdown_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockname_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getpeername_error(PRInt32 err)
+{
+
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOTCONN:
+ PR_SetError(PR_NOT_CONNECTED_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENOBUFS:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_getsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_setsockopt_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEBADF:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case WSAENOTSOCK:
+ PR_SetError(PR_NOT_SOCKET_ERROR, err);
+ break;
+ case WSAENOPROTOOPT:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAEINVAL:
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_open_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ PR_SetError(PR_FILE_EXISTS_ERROR, err);
+ break;
+ case ERROR_FILE_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_INVALID_NAME:
+ PR_SetError(PR_INVALID_ARGUMENT_ERROR, err);
+ break;
+ case ERROR_DISK_CORRUPT:
+ case ERROR_DISK_OPERATION_FAILED:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_NOT_READY:
+ case ERROR_OPEN_FAILED:
+ case ERROR_OPEN_FILES:
+ case ERROR_PATH_BUSY:
+ PR_SetError(PR_IO_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_FILENAME_EXCED_RANGE:
+ PR_SetError(PR_NAME_TOO_LONG_ERROR, err);
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err);
+ break;
+ case ERROR_PATH_NOT_FOUND:
+ PR_SetError(PR_FILE_NOT_FOUND_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_DISK_FULL:
+ case ERROR_HANDLE_DISK_FULL:
+ PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err);
+ break;
+ case ERROR_WRITE_PROTECT:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_gethostname_error(PRInt32 err)
+{
+ switch (err) {
+ case WSAEFAULT:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case WSAENETDOWN:
+ case WSAEINPROGRESS:
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}
+
+void _MD_win32_map_select_error(PRInt32 err)
+{
+ PRErrorCode prerror;
+
+ switch (err) {
+ /*
+ * Win32 select() only works on sockets. So in this
+ * context, WSAENOTSOCK is equivalent to EBADF on Unix.
+ */
+ case WSAENOTSOCK:
+ prerror = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+ case WSAEINVAL:
+ prerror = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case WSAEFAULT:
+ prerror = PR_ACCESS_FAULT_ERROR;
+ break;
+ default:
+ prerror = PR_UNKNOWN_ERROR;
+ }
+ PR_SetError(prerror, err);
+}
+
+void _MD_win32_map_lockf_error(PRInt32 err)
+{
+ switch (err) {
+ case ERROR_ACCESS_DENIED:
+ PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err);
+ break;
+ case ERROR_FILE_INVALID:
+ case ERROR_INVALID_HANDLE:
+ PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err);
+ break;
+ case ERROR_INVALID_ADDRESS:
+ case ERROR_STACK_OVERFLOW:
+ PR_SetError(PR_ACCESS_FAULT_ERROR, err);
+ break;
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_LOCKED:
+ case ERROR_SHARING_VIOLATION:
+ PR_SetError(PR_FILE_IS_LOCKED_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err);
+ break;
+ case ERROR_NOT_ENOUGH_QUOTA:
+ PR_SetError(PR_OUT_OF_MEMORY_ERROR, err);
+ break;
+ default:
+ PR_SetError(PR_UNKNOWN_ERROR, err);
+ break;
+ }
+}