diff options
Diffstat (limited to 'pr/src/md/windows')
-rw-r--r-- | pr/src/md/windows/Makefile | 75 | ||||
-rw-r--r-- | pr/src/md/windows/ntgc.c | 107 | ||||
-rw-r--r-- | pr/src/md/windows/ntinrval.c | 86 | ||||
-rw-r--r-- | pr/src/md/windows/ntio.c | 3776 | ||||
-rw-r--r-- | pr/src/md/windows/ntmisc.c | 603 | ||||
-rw-r--r-- | pr/src/md/windows/ntsem.c | 65 | ||||
-rw-r--r-- | pr/src/md/windows/ntthread.c | 434 | ||||
-rw-r--r-- | pr/src/md/windows/w16callb.c | 243 | ||||
-rw-r--r-- | pr/src/md/windows/w16error.c | 233 | ||||
-rw-r--r-- | pr/src/md/windows/w16fmem.c | 59 | ||||
-rw-r--r-- | pr/src/md/windows/w16gc.c | 67 | ||||
-rw-r--r-- | pr/src/md/windows/w16io.c | 836 | ||||
-rw-r--r-- | pr/src/md/windows/w16mem.c | 66 | ||||
-rw-r--r-- | pr/src/md/windows/w16null.c | 97 | ||||
-rw-r--r-- | pr/src/md/windows/w16proc.c | 58 | ||||
-rw-r--r-- | pr/src/md/windows/w16sock.c | 1151 | ||||
-rw-r--r-- | pr/src/md/windows/w16stdio.c | 150 | ||||
-rw-r--r-- | pr/src/md/windows/w16thred.c | 407 | ||||
-rw-r--r-- | pr/src/md/windows/w32poll.c | 189 | ||||
-rw-r--r-- | pr/src/md/windows/w95cv.c | 328 | ||||
-rw-r--r-- | pr/src/md/windows/w95io.c | 871 | ||||
-rw-r--r-- | pr/src/md/windows/w95sock.c | 614 | ||||
-rw-r--r-- | pr/src/md/windows/w95thred.c | 226 | ||||
-rw-r--r-- | pr/src/md/windows/win32_errors.c | 1104 |
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; + } +} |