diff options
Diffstat (limited to 'pr/src')
135 files changed, 63174 insertions, 0 deletions
diff --git a/pr/src/Makefile b/pr/src/Makefile new file mode 100644 index 00000000..6e514e2d --- /dev/null +++ b/pr/src/Makefile @@ -0,0 +1,316 @@ +# +# 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 + +ifdef USE_PTHREADS + DIRS = io linking malloc md memory misc pthreads threads +else + DIRS = io linking malloc md memory misc threads +endif + +# +# Define platform-dependent OS_LIBS +# + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +export OPTIMIZER +OS_LIBS = -lm +else # 4.1.3_U1 +ifeq ($(OS_RELEASE),5.4_i86pc) +OS_LIBS = -lthread -lposix4 -lsocket -lnsl -lgen -lresolv -ldl +else +ifdef USE_PTHREADS +OS_LIBS = -lpthread -lposix4 -lsocket -lnsl -ldl +else +ifdef LOCAL_THREADS_ONLY +OS_LIBS = -lsocket -lnsl -ldl +else +OS_LIBS = -lthread -lposix4 -lsocket -lnsl -ldl +endif # LOCAL_THREADS_ONLY +endif # USE_PTHREADS +endif # 5.4_i86pc +endif # 4.1.3_U1 +endif # SunOS + +ifeq ($(OS_ARCH), IRIX) +ifeq ($(USE_PTHREADS), 1) +OS_LIBS += -lpthread +endif +endif + +ifeq ($(OS_ARCH),AIX) +ifeq ($(CLASSIC_NSPR),1) +ifeq ($(OS_RELEASE),4.1) +OS_LIBS += -lsvld -lc +else +OS_LIBS += -ldl -lc +endif +else +ifeq ($(OS_RELEASE),4.1) +OS_LIBS += -lsvld -lC_r -lC -lpthreads -lc_r -lm /usr/lib/libc.a +else +OS_LIBS += -ldl -lC_r -lC -lpthreads -lc_r -lm /usr/lib/libc.a +endif +endif +endif + +# On AIX, we override malloc in non-pthread versions. On AIX 4.2 or +# above, this requires that we use the rtl-enabled version of libc.a. +ifeq ($(OS_ARCH),AIX) +ifneq ($(OS_RELEASE),4.1) +ifneq ($(USE_PTHREADS),1) +BUILD_AIX_RTL_LIBC = 1 +AIX_RTL_LIBC = $(OBJDIR)/libc.a +endif +endif +endif + +ifeq ($(OS_ARCH),OSF1) +ifneq ($(OS_RELEASE),V2.0) +OS_LIBS = -lc_r +endif +ifeq ($(USE_PTHREADS), 1) +OS_LIBS += -lpthread -lrt +endif +ifeq ($(USE_IPV6), 1) +OS_LIBS += -lip6 +endif +endif + +ifeq ($(OS_ARCH),Linux) +ifeq ($(USE_PTHREADS), 1) +OS_LIBS = -L/lib -lpthread -ldl -lc +else +OS_LIBS = -L/lib -ldl -lc +endif +endif + +ifeq ($(OS_ARCH),HP-UX) +ifeq ($(OS_RELEASE),A.09.03) +OS_LIBS = -ldld -L/lib/pa1.1 -lm +else +OS_LIBS = -ldld -lm -lc +endif +ifeq ($(USE_PTHREADS), 1) +OS_LIBS += -lpthread +endif +ifeq ($(PTHREADS_USER), 1) +OS_LIBS += -lpthread +endif +endif + +ifeq ($(OS_ARCH),UNIXWARE) +OS_LIBS = -lsocket -lc +endif + +ifeq ($(OS_ARCH),NEWS-OS) +OS_LIBS = -lsocket -lnsl -lgen -lresolv +endif + +ifeq ($(OS_ARCH),WINNT) +ifeq ($(OS_TARGET),OS2) +OS_LIBS = so32dll.lib tcp32dll.lib +else +ifneq ($(OS_TARGET),WIN16) +OS_LIBS = wsock32.lib winmm.lib +endif +endif +endif + +# +# Define platform-dependent OBJS +# + +OBJS = \ + io/$(OBJDIR)/prmwait.o \ + io/$(OBJDIR)/prmapopt.o \ + io/$(OBJDIR)/priometh.o \ + io/$(OBJDIR)/prlayer.o \ + io/$(OBJDIR)/prlog.o \ + io/$(OBJDIR)/prmmap.o \ + io/$(OBJDIR)/prprf.o \ + io/$(OBJDIR)/prscanf.o \ + io/$(OBJDIR)/prstdio.o \ + threads/$(OBJDIR)/prcmon.o \ + linking/$(OBJDIR)/prlink.o \ + malloc/$(OBJDIR)/prmalloc.o \ + malloc/$(OBJDIR)/prmem.o \ + md/$(OBJDIR)/prosdep.o \ + memory/$(OBJDIR)/prseg.o \ + misc/$(OBJDIR)/pralarm.o \ + misc/$(OBJDIR)/pratom.o \ + misc/$(OBJDIR)/prdtoa.o \ + misc/$(OBJDIR)/prenv.o \ + misc/$(OBJDIR)/prerror.o \ + misc/$(OBJDIR)/prinit.o \ + misc/$(OBJDIR)/prinrval.o \ + misc/$(OBJDIR)/prlog2.o \ + misc/$(OBJDIR)/prlong.o \ + misc/$(OBJDIR)/prnetdb.o \ + misc/$(OBJDIR)/prsystem.o \ + misc/$(OBJDIR)/prthinfo.o \ + misc/$(OBJDIR)/prtime.o + +ifdef USE_PTHREADS +OBJS += \ + pthreads/$(OBJDIR)/ptsynch.o \ + pthreads/$(OBJDIR)/ptio.o \ + pthreads/$(OBJDIR)/ptthread.o \ + pthreads/$(OBJDIR)/ptmisc.o +else +OBJS += \ + io/$(OBJDIR)/prdir.o \ + io/$(OBJDIR)/prfile.o \ + io/$(OBJDIR)/prio.o \ + io/$(OBJDIR)/prsocket.o \ + threads/$(OBJDIR)/prcthr.o \ + threads/$(OBJDIR)/prdump.o \ + threads/$(OBJDIR)/prmon.o \ + threads/$(OBJDIR)/prsem.o \ + threads/$(OBJDIR)/prtpd.o \ + threads/combined/$(OBJDIR)/prucpu.o \ + threads/combined/$(OBJDIR)/prucv.o \ + threads/combined/$(OBJDIR)/prulock.o \ + threads/combined/$(OBJDIR)/prustack.o \ + threads/combined/$(OBJDIR)/pruthr.o +endif + +ifeq ($(USE_IPV6), 1) +OBJS += io/$(OBJDIR)/pripv6.o +endif + +ifeq ($(OS_ARCH), WINNT) +ifneq (,$(filter-out WIN16 OS2,$(OS_TARGET))) +DLLBASE=/BASE:0x30000000 +RES=$(OBJDIR)/nspr.res +RESNAME=nspr.rc +endif + +ifeq ($(OS_TARGET), WIN16) +OBJS += md/windows/$(OBJDIR)/w16null.o \ + md/windows/$(OBJDIR)/w16proc.o \ + md/windows/$(OBJDIR)/w16thred.o \ + md/windows/$(OBJDIR)/w16fmem.o \ + md/windows/$(OBJDIR)/w16sock.o \ + md/windows/$(OBJDIR)/w16mem.o \ + md/windows/$(OBJDIR)/w16io.o \ + md/windows/$(OBJDIR)/w16gc.o \ + md/windows/$(OBJDIR)/w16error.o \ + md/windows/$(OBJDIR)/w16callb.o \ + md/windows/$(OBJDIR)/ntinrval.o +EXTRA_LIBS += $(MOD_DEPTH)/tools/winsock.lib +W16_EXPORTS = EXPORT _malloc.2=_PR_MD_malloc RESIDENT, \ + _realloc.3=_PR_MD_realloc RESIDENT, \ + _calloc.4=_PR_MD_calloc RESIDENT, \ + _free.5=_PR_MD_free RESIDENT, \ + _getenv.9=_PR_MD_getenv RESIDENT, \ + _printf.11=_PR_MD_printf RESIDENT, \ + _strftime.13=_PR_MD_strftime RESIDENT, \ + _sscanf.33=_PR_MD_sscanf RESIDENT, \ + _putenv.10=_PR_MD_putenv RESIDENT, \ + _fprintf.12=_PR_MD_fprintf RESIDENT +else +ifeq ($(OS_TARGET), WIN95) +OBJS += md/windows/$(OBJDIR)/w95io.o \ + md/windows/$(OBJDIR)/w95sock.o \ + md/windows/$(OBJDIR)/w95thred.o \ + md/windows/$(OBJDIR)/w95cv.o \ + md/windows/$(OBJDIR)/ntgc.o \ + md/windows/$(OBJDIR)/ntmisc.o \ + md/windows/$(OBJDIR)/ntinrval.o \ + md/windows/$(OBJDIR)/ntsem.o \ + md/windows/$(OBJDIR)/win32_errors.o \ + md/windows/$(OBJDIR)/w32poll.o +else +ifeq ($(OS_TARGET), OS2) +OBJS += md/os2/$(OBJDIR)/os2io.o \ + md/os2/$(OBJDIR)/os2sock.o \ + md/os2/$(OBJDIR)/os2thred.o \ + md/os2/$(OBJDIR)/os2cv.o \ + md/os2/$(OBJDIR)/os2gc.o \ + md/os2/$(OBJDIR)/os2misc.o \ + md/os2/$(OBJDIR)/os2inrval.o \ + md/os2/$(OBJDIR)/os2sem.o \ + md/os2/$(OBJDIR)/os2_errors.o \ + md/os2/$(OBJDIR)/os2poll.o \ + md/os2/$(OBJDIR)/ramsem.o \ + md/os2/$(OBJDIR)/sem_x86.o +else +OBJS += md/windows/$(OBJDIR)/ntio.o \ + md/windows/$(OBJDIR)/ntgc.o \ + md/windows/$(OBJDIR)/ntthread.o \ + md/windows/$(OBJDIR)/ntmisc.o \ + md/windows/$(OBJDIR)/ntinrval.o \ + md/windows/$(OBJDIR)/ntsem.o \ + md/windows/$(OBJDIR)/win32_errors.o \ + md/windows/$(OBJDIR)/w32poll.o +endif +endif +endif + +else + + THREAD_DIR=threads/combined/$(OBJDIR) + +ifeq ($(OS_ARCH), MAC) + MD_DIR = md/mac/$(OBJDIR) +else + MD_DIR = md/unix/$(OBJDIR) + include md/unix/objs.mk +endif + +endif + +LIBRARY_NAME = nspr +LIBRARY_VERSION = $(MOD_VERSION) + +RELEASE_LIBS = $(TARGETS) + +include $(MOD_DEPTH)/config/rules.mk + +ifeq ($(BUILD_AIX_RTL_LIBC),1) +TARGETS += $(AIX_RTL_LIBC) +# XXX is this a shared library? +endif + +# +# The Client build wants the shared libraries in $(DIST)/bin, +# so we also install them there. +# + +export:: $(TARGETS) + $(INSTALL) -m 444 $(TARGETS) $(DIST)/lib + $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/bin +ifeq ($(MOZ_BITS),16) + $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/lib + $(INSTALL) -m 444 $(TARGETS) $(MOZ_DIST)/bin +endif + +ifeq ($(BUILD_AIX_RTL_LIBC),1) +$(AIX_RTL_LIBC): /usr/ccs/lib/libc.a + rtl_enable -o $@ $< +endif + +install:: export diff --git a/pr/src/io/Makefile b/pr/src/io/Makefile new file mode 100644 index 00000000..68dda471 --- /dev/null +++ b/pr/src/io/Makefile @@ -0,0 +1,68 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + prmwait.c \ + priometh.c \ + prmapopt.c \ + prlayer.c \ + prlog.c \ + prmmap.c \ + prprf.c \ + prscanf.c \ + prstdio.c \ + $(NULL) + +ifndef USE_PTHREADS + CSRCS += \ + prdir.c \ + prfile.c \ + prio.c \ + prlog.c \ + prmmap.c \ + prprf.c \ + prsocket.c \ + prstdio.c \ + $(NULL) +endif + +ifdef USE_IPV6 +CSRCS += pripv6.c +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/io/pprmwait.h b/pr/src/io/pprmwait.h new file mode 100644 index 00000000..55b258e0 --- /dev/null +++ b/pr/src/io/pprmwait.h @@ -0,0 +1,77 @@ +/* -*- 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. + */ + +#if defined(_PPRMWAIT_H) +#else +#define _PPRMWAIT_H + +#include "prlock.h" +#include "prcvar.h" +#include "prclist.h" +#include "prthread.h" + +#define _PR_HASH_OFFSET 75013 +#define MAX_POLLING_INTERVAL 100 +#define _PR_POLL_COUNT_FUDGE 64 +#define MAX_POLLING_INTERVAL 100 +#define _PR_DEFAULT_HASH_LENGTH 59 + +#define _MW_REHASH(a, i, m) _MW_HASH((PRUint32)(a) + (i) + _PR_HASH_OFFSET, m) +#define _MW_HASH(a, m) ((((PRUint32)(a) >> 4) ^ ((PRUint32)(a) >> 10)) % (m)) +#define _MW_ABORTED(_rv) \ + ((PR_FAILURE == (_rv)) && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) + +typedef enum {_prmw_success, _prmw_rehash, _prmw_error} _PR_HashStory; + +typedef struct _PRWaiterHash +{ + PRUint16 count; /* current number in hash table */ + PRUint16 length; /* current size of the hash table */ + PRRecvWait *recv_wait; /* hash table of receive wait objects */ +} _PRWaiterHash; + +typedef enum {_prmw_running, _prmw_stopping, _prmw_stopped} PRMWGroupState; + +struct PRWaitGroup +{ + PRCList group_link; /* all groups are linked to each other */ + PRCList io_ready; /* list of I/O requests that are ready */ + PRMWGroupState state; /* state of this group (so we can shut down) */ + + PRLock *ml; /* lock for synchronizing this wait group */ + PRCondVar *io_taken; /* calling threads notify when they take I/O */ + PRCondVar *io_complete; /* calling threads wait here for completions */ + PRCondVar *new_business; /* polling thread waits here more work */ + PRCondVar *mw_manage; /* used to manage group lists */ + PRThread* poller; /* thread that's actually doing the poll() */ + PRUint16 waiting_threads; /* number of threads waiting for recv */ + PRUint16 polling_count; /* number of elements in the polling list */ + PRPollDesc *polling_list; /* list poller builds for polling */ + PRIntervalTime last_poll; /* last time we polled */ + _PRWaiterHash *waiter; /* pointer to hash table of wait receive objects */ +}; + +typedef struct _PRGlobalState +{ + PRCList group_list; /* master of the group list */ + PRWaitGroup *group; /* the default (NULL) group */ +} _PRGlobalState; + +#endif /* defined(_PPRMWAIT_H) */ + +/* pprmwait.h */ diff --git a/pr/src/io/prdir.c b/pr/src/io/prdir.c new file mode 100644 index 00000000..4d6a91fd --- /dev/null +++ b/pr/src/io/prdir.c @@ -0,0 +1,83 @@ +/* -*- 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" + +PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name) +{ + PRDir *dir; + PRStatus sts; + + dir = PR_NEW(PRDir); + if (dir) { + sts = _PR_MD_OPEN_DIR(&dir->md,name); + if (sts != PR_SUCCESS) { + PR_DELETE(dir); + return NULL; + } + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return dir; +} + +PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) +{ + /* _MD_READ_DIR return a char* to the name; allocation in machine-dependent code */ + char* name = _PR_MD_READ_DIR(&dir->md, flags); + dir->d.name = name; + return name ? &dir->d : NULL; +} + +PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir) +{ +PRInt32 rv; + + if (dir) { + rv = _PR_MD_CLOSE_DIR(&dir->md); + PR_DELETE(dir); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode) +{ +PRInt32 rv; + + rv = _PR_MD_MKDIR(name, mode); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name) +{ +PRInt32 rv; + + rv = _PR_MD_RMDIR(name); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + diff --git a/pr/src/io/prfile.c b/pr/src/io/prfile.c new file mode 100644 index 00000000..ab20b2a3 --- /dev/null +++ b/pr/src/io/prfile.c @@ -0,0 +1,554 @@ +/* -*- 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 <string.h> +#include <fcntl.h> + +#ifdef XP_UNIX +#ifdef AIX +/* To pick up sysconf */ +#include <unistd.h> +#else +/* To pick up getrlimit, setrlimit */ +#include <sys/time.h> +#include <sys/resource.h> +#endif +#endif /* XP_UNIX */ + +extern PRLock *_pr_flock_lock; + +static PRInt32 PR_CALLBACK FileRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRInt32 rv = 0; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + rv = -1; + } + if (rv == -1) + return rv; + + rv = _PR_MD_READ(fd, buf, amount); + if (rv < 0) { + PR_ASSERT(rv == -1); + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("read -> %d", rv)); + return rv; +} + +static PRInt32 PR_CALLBACK FileWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRInt32 rv = 0; + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + rv = -1; + } + if (rv != 0) + return rv; + + count = 0; + while (amount > 0) { + temp = _PR_MD_WRITE(fd, buf, amount); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("write -> %d", count)); + return count; +} + +static PRInt32 PR_CALLBACK FileSeek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + PRInt32 result; + + result = _PR_MD_LSEEK(fd, offset, whence); + return result; +} + +static PRInt64 PR_CALLBACK FileSeek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + PRInt64 result; + + result = _PR_MD_LSEEK64(fd, offset, whence); + return result; +} + +static PRInt32 PR_CALLBACK FileAvailable(PRFileDesc *fd) +{ + PRInt32 result, cur, end; + + cur = _PR_MD_LSEEK(fd, 0, PR_SEEK_CUR); + + if (cur >= 0) + end = _PR_MD_LSEEK(fd, 0, PR_SEEK_END); + + if ((cur < 0) || (end < 0)) { + return -1; + } + + result = end - cur; + _PR_MD_LSEEK(fd, cur, PR_SEEK_SET); + + return result; +} + +static PRInt64 PR_CALLBACK FileAvailable64(PRFileDesc *fd) +{ + PRInt64 result, cur, end; + PRInt64 minus_one; + + LL_I2L(minus_one, -1); + cur = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_CUR); + + if (LL_GE_ZERO(cur)) + end = _PR_MD_LSEEK64(fd, LL_ZERO, PR_SEEK_END); + + if (!LL_GE_ZERO(cur) || !LL_GE_ZERO(end)) return minus_one; + + LL_SUB(result, end, cur); + (void)_PR_MD_LSEEK64(fd, cur, PR_SEEK_SET); + + return result; +} + +static PRStatus PR_CALLBACK FileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + PRInt32 rv; + + rv = _PR_MD_GETOPENFILEINFO(fd, info); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK FileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + /* $$$$ NOT YET IMPLEMENTED */ + PRInt32 rv; + + rv = _PR_MD_GETOPENFILEINFO64(fd, info); + if (rv < 0) return PR_FAILURE; + else return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK FileSync(PRFileDesc *fd) +{ + PRInt32 result; + result = _PR_MD_FSYNC(fd); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK FileClose(PRFileDesc *fd) +{ + PRInt32 rv; + + if (!fd || fd->secret->state != _PR_FILEDESC_OPEN) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + + fd->secret->state = _PR_FILEDESC_CLOSED; + + rv = _PR_MD_CLOSE_FILE(fd->secret->md.osfd); + PR_FreeFileDesc(fd); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PRIOMethods _pr_fileMethods = { + PR_DESC_FILE, + FileClose, + FileRead, + FileWrite, + FileAvailable, + FileAvailable64, + FileSync, + FileSeek, + FileSeek64, + FileInfo, + FileInfo64, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + (PRPollFN)0, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRGetsockoptFN)_PR_InvalidStatus, + (PRSetsockoptFN)_PR_InvalidStatus, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus, +}; + +PR_IMPLEMENT(PRIOMethods*) PR_GetFileMethods(void) +{ + return &_pr_fileMethods; +} + +PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode) +{ + PRInt32 osfd; + PRFileDesc *fd = 0; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + /* Map pr open flags and mode to os specific flags */ + + osfd = _PR_MD_OPEN(name, flags, mode); + if (osfd != -1) { + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if (!fd) { + (void) _PR_MD_CLOSE_FILE(osfd); + } + } + return fd; +} + +PRInt32 PR_GetSysfdTableMax(void) +{ +#if defined(XP_UNIX) && !defined(AIX) + struct rlimit rlim; + + if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) { + /* XXX need to call PR_SetError() */ + return -1; + } + + return rlim.rlim_max; +#elif defined(AIX) + return sysconf(_SC_OPEN_MAX); +#elif defined(WIN32) || defined(OS2) + /* + * There is a systemwide limit of 65536 user handles. + * Not sure on OS/2, but sounds good. + */ + return 16384; +#elif defined (WIN16) + return FOPEN_MAX; +#elif defined (XP_MAC) + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#else + write me; +#endif +} + +PRInt32 PR_SetSysfdTableSize(int table_size) +{ +#if defined(XP_UNIX) && !defined(AIX) + struct rlimit rlim; + PRInt32 tableMax = PR_GetSysfdTableMax(); + + if (tableMax < 0) + return -1; + + if (tableMax > FD_SETSIZE) + tableMax = FD_SETSIZE; + + rlim.rlim_max = tableMax; + + /* Grow as much as we can; even if too big */ + if ( rlim.rlim_max < table_size ) + rlim.rlim_cur = rlim.rlim_max; + else + rlim.rlim_cur = table_size; + + if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) { + /* XXX need to call PR_SetError() */ + return -1; + } + + return rlim.rlim_cur; +#elif defined(AIX) || defined(WIN32) || defined(WIN16) || defined(OS2) + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#elif defined (XP_MAC) +#pragma unused (table_size) + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return -1; +#else + write me; +#endif +} + +PR_IMPLEMENT(PRStatus) PR_Delete(const char *name) +{ + PRInt32 rv; + + rv = _PR_MD_DELETE(name); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv; + + rv = _PR_MD_GETFILEINFO(fn, info); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo64(const char *fn, PRFileInfo64 *info) +{ + PRInt32 rv; + + rv = _PR_MD_GETFILEINFO64(fn, info); + if (rv < 0) { + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} + +PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to) +{ + PRInt32 rv; + + rv = _PR_MD_RENAME(from, to); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how) +{ +PRInt32 rv; + + rv = _PR_MD_ACCESS(name, how); + if (rv < 0) { + return PR_FAILURE; + } else + return PR_SUCCESS; +} + +/* +** Import an existing OS file to NSPR +*/ +PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd) +{ + PRFileDesc *fd = NULL; + + fd = PR_AllocFileDesc(osfd, &_pr_fileMethods); + if( !fd ) { + (void) _PR_MD_CLOSE_FILE(osfd); + } + + return fd; +} + +#ifndef NO_NSPR_10_SUPPORT +/* +** PR_Stat() for Win16 is defined in w16io.c +** it is a hack to circumvent problems in Gromit and Java +** See also: BugSplat: 98516. +*/ +#if !defined(WIN16) +/* + * This function is supposed to be for backward compatibility with + * nspr 1.0. Therefore, it still uses the nspr 1.0 error-reporting + * mechanism -- returns a PRInt32, which is the error code when the call + * fails. + * + * If we need this function in nspr 2.0, it should be changed to + * return PRStatus, as follows: + * + * PR_IMPLEMENT(PRStatus) PR_Stat(const char *name, struct stat *buf) + * { + * PRInt32 rv; + * + * rv = _PR_MD_STAT(name, buf); + * if (rv < 0) + * return PR_FAILURE; + * else + * return PR_SUCCESS; + * } + * + * -- wtc, 2/14/97. + */ +PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf) +{ + PRInt32 rv; + + rv = _PR_MD_STAT(name, buf); + return rv; +} + +#endif /* !defined(WIN16) */ +#endif /* ! NO_NSPR_10_SUPPORT */ + +PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd) +{ + PRStatus rv = PR_SUCCESS; + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 0) { + rv = _PR_MD_LOCKFILE(fd->secret->md.osfd); + if (rv == PR_SUCCESS) + fd->secret->lockCount = 1; + } else { + fd->secret->lockCount++; + } + PR_Unlock(_pr_flock_lock); + + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd) +{ + PRStatus rv = PR_SUCCESS; + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 0) { + rv = _PR_MD_TLOCKFILE(fd->secret->md.osfd); + PR_ASSERT(rv == PR_SUCCESS || fd->secret->lockCount == 0); + if (rv == PR_SUCCESS) + fd->secret->lockCount = 1; + } else { + fd->secret->lockCount++; + } + PR_Unlock(_pr_flock_lock); + + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd) +{ + PRStatus rv = PR_SUCCESS; + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 1) { + rv = _PR_MD_UNLOCKFILE(fd->secret->md.osfd); + if (rv == PR_SUCCESS) + fd->secret->lockCount = 0; + } else { + fd->secret->lockCount--; + } + PR_Unlock(_pr_flock_lock); + + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +) +{ +#if defined(XP_MAC) +#pragma unused (readPipe, writePipe) +#endif + +#ifdef WIN32 + HANDLE readEnd, writeEnd; + SECURITY_ATTRIBUTES pipeAttributes; + + ZeroMemory(&pipeAttributes, sizeof(pipeAttributes)); + pipeAttributes.nLength = sizeof(pipeAttributes); + pipeAttributes.bInheritHandle = TRUE; + if (CreatePipe(&readEnd, &writeEnd, &pipeAttributes, 0) == 0) { + PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); + return PR_FAILURE; + } + *readPipe = PR_AllocFileDesc((PRInt32)readEnd, &_pr_fileMethods); + if (NULL == *readPipe) { + CloseHandle(readEnd); + CloseHandle(writeEnd); + return PR_FAILURE; + } + *writePipe = PR_AllocFileDesc((PRInt32)writeEnd, &_pr_fileMethods); + if (NULL == *writePipe) { + PR_Close(*readPipe); + CloseHandle(writeEnd); + return PR_FAILURE; + } +#ifdef WINNT + (*readPipe)->secret->md.nonoverlapped = PR_TRUE; + (*writePipe)->secret->md.nonoverlapped = PR_TRUE; +#endif + return PR_SUCCESS; +#elif defined(XP_UNIX) + int pipefd[2]; + + if (pipe(pipefd) == -1) { + /* XXX map pipe error */ + PR_SetError(PR_UNKNOWN_ERROR, errno); + return PR_FAILURE; + } + *readPipe = PR_AllocFileDesc(pipefd[0], &_pr_fileMethods); + if (NULL == *readPipe) { + close(pipefd[0]); + close(pipefd[1]); + return PR_FAILURE; + } + *writePipe = PR_AllocFileDesc(pipefd[1], &_pr_fileMethods); + if (NULL == *writePipe) { + PR_Close(*readPipe); + close(pipefd[1]); + return PR_FAILURE; + } + _MD_MakeNonblock(*readPipe); + _MD_MakeNonblock(*writePipe); + return PR_SUCCESS; +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} diff --git a/pr/src/io/prio.c b/pr/src/io/prio.c new file mode 100644 index 00000000..123b3854 --- /dev/null +++ b/pr/src/io/prio.c @@ -0,0 +1,184 @@ +/* -*- 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 <string.h> /* for memset() */ + + +/************************************************************************/ + +PRLock *_pr_flock_lock; + +PRFileDesc *_pr_filedesc_freelist; +PRLock *_pr_filedesc_freelist_lock; + +void _PR_InitIO(void) +{ + PRIOMethods *methods = PR_GetFileMethods(); + + _pr_filedesc_freelist = NULL; + _pr_filedesc_freelist_lock = PR_NewLock(); + _pr_flock_lock = PR_NewLock(); + +#ifdef WIN32 + _pr_stdin = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_INPUT_HANDLE), + methods); + _pr_stdout = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_OUTPUT_HANDLE), + methods); + _pr_stderr = PR_AllocFileDesc((PRInt32)GetStdHandle(STD_ERROR_HANDLE), + methods); +#ifdef WINNT + _pr_stdin->secret->md.nonoverlapped = PR_TRUE; + _pr_stdout->secret->md.nonoverlapped = PR_TRUE; + _pr_stderr->secret->md.nonoverlapped = PR_TRUE; +#endif +#else + _pr_stdin = PR_AllocFileDesc(0, methods); + _pr_stdout = PR_AllocFileDesc(1, methods); + _pr_stderr = PR_AllocFileDesc(2, methods); +#endif + + _PR_MD_INIT_IO(); +} + +PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) +{ + PRFileDesc *result = NULL; + PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError); + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + switch (osfd) + { + case PR_StandardInput: result = _pr_stdin; break; + case PR_StandardOutput: result = _pr_stdout; break; + case PR_StandardError: result = _pr_stderr; break; + default: + (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + return result; +} + +PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(PRInt32 osfd, PRIOMethods *methods) +{ + PRFileDesc *fd; + +#ifdef XP_UNIX + /* + * Assert that the file descriptor is small enough to fit in the + * fd_set passed to select + */ + PR_ASSERT(osfd < FD_SETSIZE); +#endif + if (_pr_filedesc_freelist) { + PR_Lock(_pr_filedesc_freelist_lock); + if (_pr_filedesc_freelist) { + PRFilePrivate *secretSave; + fd = _pr_filedesc_freelist; + _pr_filedesc_freelist = _pr_filedesc_freelist->secret->next; + PR_Unlock(_pr_filedesc_freelist_lock); + secretSave = fd->secret; + memset(fd, 0, sizeof(PRFileDesc)); + memset(secretSave, 0, sizeof(PRFilePrivate)); + fd->secret = secretSave; + } else { + PR_Unlock(_pr_filedesc_freelist_lock); + fd = PR_NEWZAP(PRFileDesc); + fd->secret = PR_NEWZAP(PRFilePrivate); + } + } else { + fd = PR_NEWZAP(PRFileDesc); + fd->secret = PR_NEWZAP(PRFilePrivate); + } + if (fd) { + /* Initialize the members of PRFileDesc and PRFilePrivate */ + fd->methods = methods; + fd->secret->state = _PR_FILEDESC_OPEN; + fd->secret->md.osfd = osfd; + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + + return fd; +} + +PR_IMPLEMENT(void) PR_FreeFileDesc(PRFileDesc *fd) +{ + PR_ASSERT(fd); + PR_ASSERT(fd->secret->state == _PR_FILEDESC_CLOSED); + + fd->secret->state = _PR_FILEDESC_FREED; + + PR_Lock(_pr_filedesc_freelist_lock); + + /* Add to head of list- this is a LIFO structure to avoid constantly + * using different structs + */ + fd->secret->next = _pr_filedesc_freelist; + _pr_filedesc_freelist = fd; + + PR_Unlock(_pr_filedesc_freelist_lock); +} + +#ifdef XP_UNIX +#include <fcntl.h> /* to pick up F_GETFL */ +#endif + +/* +** Wait for some i/o to finish on one or more more poll descriptors. +*/ +PR_IMPLEMENT(PRInt32) PR_Poll(PRPollDesc *pds, PRIntn npds, + PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRInt32 n; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + /* + ** Before we do the poll operation, check each descriptor and see if + ** it needs special poll handling. If it does, use the method poll + ** proc to check for data before blocking. + */ + pd = pds; + n = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRFileDesc *fd = pd->fd; + PRInt16 in_flags; + if (NULL == fd) { + continue; + } + in_flags = pd->in_flags; + if (in_flags && fd->methods->poll) { + PRInt16 out_flags = (*fd->methods->poll)(fd, in_flags); + if (out_flags) { + pd->out_flags = out_flags; + n++; + } + } + } + if (n != 0) { + return n; + } + return(_PR_MD_PR_POLL(pds, npds, timeout)); +} diff --git a/pr/src/io/priometh.c b/pr/src/io/priometh.c new file mode 100644 index 00000000..a65b5c53 --- /dev/null +++ b/pr/src/io/priometh.c @@ -0,0 +1,232 @@ +/* -*- 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" + +/* + * An invalid method that returns an integer + */ + +PRIntn _PR_InvalidInt() +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return -1; +} /* _PR_InvalidInt */ + +PRInt64 _PR_InvalidInt64() +{ + PRInt64 rv = LL_INIT( 0xffffffff, 0xffffffff ); + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return rv; +} /* _PR_InvalidInt */ + +/* + * An invalid method that returns PRStatus + */ + +PRStatus _PR_InvalidStatus() +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return PR_FAILURE; +} /* _PR_InvalidDesc */ + +/* + * An invalid method that returns a pointer + */ + +PRFileDesc *_PR_InvalidDesc() +{ + PR_SetError(PR_INVALID_METHOD_ERROR, 0); + return NULL; +} /* _PR_InvalidDesc */ + +PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file) +{ + return file->methods->file_type; +} + +PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd) +{ + return (fd->methods->close)(fd); +} + +PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return((fd->methods->read)(fd,buf,amount)); +} + +PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return((fd->methods->write)(fd,buf,amount)); +} + +PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + return((fd->methods->seek)(fd, offset, whence)); +} + +PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + return((fd->methods->seek64)(fd, offset, whence)); +} + +PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd) +{ + return((fd->methods->available)(fd)); +} + +PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd) +{ + return((fd->methods->available64)(fd)); +} + +PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + return((fd->methods->fileInfo)(fd, info)); +} + +PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + return((fd->methods->fileInfo64)(fd, info)); +} + +PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd) +{ + return((fd->methods->fsync)(fd)); +} + +PR_IMPLEMENT(PRStatus) PR_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->connect)(fd,addr,timeout)); +} + +PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr, +PRIntervalTime timeout) +{ + return((fd->methods->accept)(fd,addr,timeout)); +} + +PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr) +{ + return((fd->methods->bind)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how) +{ + return((fd->methods->shutdown)(fd,how)); +} + +PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog) +{ + return((fd->methods->listen)(fd,backlog)); +} + +PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount, +PRIntn flags, PRIntervalTime timeout) +{ + return((fd->methods->recv)(fd,buf,amount,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, +PRIntn flags, PRIntervalTime timeout) +{ + return((fd->methods->send)(fd,buf,amount,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, PRIOVec *iov,PRInt32 iov_size, +PRIntervalTime timeout) +{ + if (iov_size > PR_MAX_IOVECTOR_SIZE) + { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return -1; + } + return((fd->methods->writev)(fd,iov,iov_size,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, +PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_SendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_TransmitFile( + PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout)); +} + +PR_IMPLEMENT(PRInt32) PR_AcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout)); +} + +PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr) +{ + return((fd->methods->getsockname)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + return((fd->methods->getpeername)(fd,addr)); +} + +PR_IMPLEMENT(PRStatus) PR_GetSockOpt( + PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen) +{ +#if defined(DEBUG) + static PRBool warn = PR_TRUE; + if (warn) warn = _PR_Obsolete("PR_GetSockOpt()", "PR_GetSocketOption()"); +#endif + return((fd->methods->getsockopt)(fd, optname, optval, optlen)); +} + +PR_IMPLEMENT(PRStatus) PR_SetSockOpt( + PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen) +{ +#if defined(DEBUG) + static PRBool warn = PR_TRUE; + if (warn) warn = _PR_Obsolete("PR_SetSockOpt()", "PR_SetSocketOption()"); +#endif + return((fd->methods->setsockopt)(fd, optname, optval, optlen)); +} + +PR_IMPLEMENT(PRStatus) PR_GetSocketOption( + PRFileDesc *fd, PRSocketOptionData *data) +{ + return((fd->methods->getsocketoption)(fd, data)); +} + +PR_IMPLEMENT(PRStatus) PR_SetSocketOption( + PRFileDesc *fd, const PRSocketOptionData *data) +{ + return((fd->methods->setsocketoption)(fd, data)); +} + +/* priometh.c */ diff --git a/pr/src/io/pripv6.c b/pr/src/io/pripv6.c new file mode 100644 index 00000000..b2685e56 --- /dev/null +++ b/pr/src/io/pripv6.c @@ -0,0 +1,26 @@ +/* -*- 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. + */ + +/* +** File: pripv6.c +** Description: Support for various functions unique to IPv6 +*/ + + +/* pripv6.c */ + diff --git a/pr/src/io/prlayer.c b/pr/src/io/prlayer.c new file mode 100644 index 00000000..d70a935e --- /dev/null +++ b/pr/src/io/prlayer.c @@ -0,0 +1,574 @@ +/* -*- 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. + */ + +/* +** File: prlayer.c +** Description: Routines for handling pushable protocol modules on sockets. +*/ + +#include "primpl.h" +#include "prerror.h" +#include "prmem.h" +#include "prlock.h" +#include "prlog.h" +#include "prio.h" + +#include <string.h> /* for memset() */ + +void PR_CALLBACK pl_FDDestructor(PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + if (NULL != fd->lower) fd->lower->higher = fd->higher; + if (NULL != fd->higher) fd->higher->lower = fd->lower; + PR_DELETE(fd); +} + +/* +** Default methods that just call down to the next fd. +*/ +static PRStatus PR_CALLBACK pl_TopClose (PRFileDesc *fd) +{ + PRStatus status; + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + PR_ASSERT(fd->secret == NULL); + PR_ASSERT(fd->methods->file_type == PR_DESC_LAYERED); + + status = (fd->lower->methods->close)(fd->lower); + + fd->dtor(fd); + return status; +} + +static PRInt32 PR_CALLBACK pl_DefRead (PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->read)(fd->lower, buf, amount); +} + +static PRInt32 PR_CALLBACK pl_DefWrite ( + PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->write)(fd->lower, buf, amount); +} + +static PRInt32 PR_CALLBACK pl_DefAvailable (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->available)(fd->lower); +} + +static PRInt64 PR_CALLBACK pl_DefAvailable64 (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->available64)(fd->lower); +} + +static PRStatus PR_CALLBACK pl_DefFsync (PRFileDesc *fd) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fsync)(fd->lower); +} + +static PRInt32 PR_CALLBACK pl_DefSeek ( + PRFileDesc *fd, PRInt32 offset, PRSeekWhence how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->seek)(fd->lower, offset, how); +} + +static PRInt64 PR_CALLBACK pl_DefSeek64 ( + PRFileDesc *fd, PRInt64 offset, PRSeekWhence how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->seek64)(fd->lower, offset, how); +} + +static PRStatus PR_CALLBACK pl_DefFileInfo (PRFileDesc *fd, PRFileInfo *info) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fileInfo)(fd->lower, info); +} + +static PRStatus PR_CALLBACK pl_DefFileInfo64 (PRFileDesc *fd, PRFileInfo64 *info) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->fileInfo64)(fd->lower, info); +} + +static PRInt32 PR_CALLBACK pl_DefWritev (PRFileDesc *fd, PRIOVec *iov, PRInt32 size, + PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->writev)(fd->lower, iov, size, timeout); +} + +static PRStatus PR_CALLBACK pl_DefConnect ( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->connect)(fd->lower, addr, timeout); +} + +static PRFileDesc * PR_CALLBACK pl_TopAccept ( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRFileDesc *newfd; + + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + newfd = (fd->lower->methods->accept)(fd->lower, addr, timeout); + if (newfd != NULL) + { + if (PR_FAILURE == PR_PushIOLayer(fd, fd->identity, newfd)) + { + PR_Close(newfd); + newfd = NULL; + } + } + return newfd; +} + +static PRStatus PR_CALLBACK pl_DefBind (PRFileDesc *fd, const PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->bind)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefListen (PRFileDesc *fd, PRIntn backlog) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->listen)(fd->lower, backlog); +} + +static PRStatus PR_CALLBACK pl_DefShutdown (PRFileDesc *fd, PRIntn how) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->shutdown)(fd->lower, how); +} + +static PRInt32 PR_CALLBACK pl_DefRecv ( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->recv)( + fd->lower, buf, amount, flags, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefSend ( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->send)(fd->lower, buf, amount, flags, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefRecvfrom ( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->recvfrom)( + fd->lower, buf, amount, flags, addr, timeout); +} + +static PRInt32 PR_CALLBACK pl_DefSendto ( + PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, + const PRNetAddr *addr, PRIntervalTime timeout) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->sendto)( + fd->lower, buf, amount, flags, addr, timeout); +} + +static PRInt16 PR_CALLBACK pl_DefPoll (PRFileDesc *fd, PRInt16 how_flags) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->poll)(fd->lower, how_flags); +} + +static PRInt32 PR_CALLBACK pl_DefAcceptread ( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, void *buf, + PRInt32 amount, PRIntervalTime t) +{ + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + return sd->lower->methods->acceptread(sd->lower, nd, raddr, buf, amount, t); +} + +static PRInt32 PR_CALLBACK pl_DefTransmitfile ( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, PRInt32 hlen, + PRTransmitFileFlags flags, PRIntervalTime t) +{ + PR_ASSERT(sd != NULL); + PR_ASSERT(sd->lower != NULL); + + return sd->lower->methods->transmitfile( + sd->lower, fd, headers, hlen, flags, t); +} + +static PRStatus PR_CALLBACK pl_DefGetsockname (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getsockname)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefGetpeername (PRFileDesc *fd, PRNetAddr *addr) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getpeername)(fd->lower, addr); +} + +static PRStatus PR_CALLBACK pl_DefGetsockopt ( + PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getsockopt)(fd->lower, optname, optval, optlen); +} + +static PRStatus PR_CALLBACK pl_DefSetsockopt ( + PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->setsockopt)(fd->lower, optname, optval, optlen); +} + +static PRStatus PR_CALLBACK pl_DefGetsocketoption ( + PRFileDesc *fd, PRSocketOptionData *data) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->getsocketoption)(fd->lower, data); +} + +static PRStatus PR_CALLBACK pl_DefSetsocketoption ( + PRFileDesc *fd, const PRSocketOptionData *data) +{ + PR_ASSERT(fd != NULL); + PR_ASSERT(fd->lower != NULL); + + return (fd->lower->methods->setsocketoption)(fd->lower, data); +} + +/* Methods for the top of the stack. Just call down to the next fd. */ +static struct PRIOMethods pl_methods = { + PR_DESC_LAYERED, + pl_TopClose, + pl_DefRead, + pl_DefWrite, + pl_DefAvailable, + pl_DefAvailable64, + pl_DefFsync, + pl_DefSeek, + pl_DefSeek64, + pl_DefFileInfo, + pl_DefFileInfo64, + pl_DefWritev, + pl_DefConnect, + pl_TopAccept, + pl_DefBind, + pl_DefListen, + pl_DefShutdown, + pl_DefRecv, + pl_DefSend, + pl_DefRecvfrom, + pl_DefSendto, + pl_DefPoll, + pl_DefAcceptread, + pl_DefTransmitfile, + pl_DefGetsockname, + pl_DefGetpeername, + pl_DefGetsockopt, + pl_DefSetsockopt, + pl_DefGetsocketoption, + pl_DefSetsocketoption +}; + +PR_IMPLEMENT(PRIOMethods const*) PR_GetDefaultIOMethods() +{ + return &pl_methods; +} /* PR_GetDefaultIOMethods */ + +PR_IMPLEMENT(PRFileDesc*) PR_CreateIOLayerStub( + PRDescIdentity ident, PRIOMethods const *methods) +{ + PRFileDesc *fd = NULL; + PR_ASSERT((PR_NSPR_IO_LAYER != ident) && (PR_TOP_IO_LAYER != ident)); + if ((PR_NSPR_IO_LAYER == ident) || (PR_TOP_IO_LAYER == ident)) + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + else + { + fd = PR_NEWZAP(PRFileDesc); + if (NULL == fd) + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + else + { + fd->methods = methods; + fd->dtor = pl_FDDestructor; + fd->identity = ident; + } + } + return fd; +} /* PR_CreateIOLayerStub */ + +PR_IMPLEMENT(PRStatus) PR_PushIOLayer( + PRFileDesc *stack, PRDescIdentity id, PRFileDesc *fd) +{ + PRFileDesc *insert = PR_GetIdentitiesLayer(stack, id); + + PR_ASSERT(fd != NULL); + PR_ASSERT(stack != NULL); + PR_ASSERT(insert != NULL); + if ((NULL == stack) || (NULL == fd) || (NULL == insert)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (stack == insert) + { + /* going on top of the stack */ + PRFileDesc copy = *stack; + *stack = *fd; + *fd = copy; + fd->higher = stack; + stack->lower = fd; + } + else + { + /* going somewhere in the middle of the stack */ + fd->lower = insert; + fd->higher = insert->higher; + + insert->higher = fd; + insert->higher->lower = fd; + } + + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRFileDesc*) PR_PopIOLayer(PRFileDesc *stack, PRDescIdentity id) +{ + PRFileDesc *extract = PR_GetIdentitiesLayer(stack, id); + + PR_ASSERT(0 != id); + PR_ASSERT(NULL != stack); + PR_ASSERT(NULL != extract); + if ((NULL == stack) || (0 == id) || (NULL == extract)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + if (extract == stack) + { + /* popping top layer of the stack */ + PRFileDesc copy = *stack; + extract = stack->lower; + *stack = *extract; + *extract = copy; + stack->higher = NULL; + } + else + { + extract->lower->higher = extract->higher; + extract->higher->lower = extract->lower; + } + extract->higher = extract->lower = NULL; + return extract; +} /* PR_PopIOLayer */ + +#define ID_CACHE_INCREMENT 16 +typedef struct _PRIdentity_cache +{ + PRLock *ml; + char **name; + PRIntn length; + PRDescIdentity ident; +} _PRIdentity_cache; + +static _PRIdentity_cache identity_cache; + +PR_IMPLEMENT(PRDescIdentity) PR_GetUniqueIdentity(const char *layer_name) +{ + PRDescIdentity identity, length; + char **names = NULL, *name = NULL, **old = NULL; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + PR_ASSERT((PRDescIdentity)0x7fff > identity_cache.ident); + + if (NULL != layer_name) + { + name = (char*)PR_Malloc(strlen(layer_name) + 1); + if (NULL == name) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_INVALID_IO_LAYER; + } + strcpy(name, layer_name); + } + + /* this initial code runs unsafe */ +retry: + PR_ASSERT(NULL == names); + length = identity_cache.length; + if (length < (identity_cache.ident + 1)) + { + length += ID_CACHE_INCREMENT; + names = (char**)PR_CALLOC(length * sizeof(char*)); + if (NULL == names) + { + if (NULL != name) PR_DELETE(name); + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_INVALID_IO_LAYER; + } + } + + /* now we get serious about thread safety */ + PR_Lock(identity_cache.ml); + PR_ASSERT(identity_cache.ident <= identity_cache.length); + identity = identity_cache.ident + 1; + if (identity > identity_cache.length) /* there's no room */ + { + /* we have to do something - hopefully it's already done */ + if ((NULL != names) && (length >= identity)) + { + /* what we did is still okay */ + memcpy( + names, identity_cache.name, + identity_cache.length * sizeof(char*)); + old = identity_cache.name; + identity_cache.name = names; + identity_cache.length = length; + names = NULL; + } + else + { + PR_ASSERT(identity_cache.ident <= identity_cache.length); + PR_Unlock(identity_cache.ml); + if (NULL != names) PR_DELETE(names); + goto retry; + } + } + if (NULL != name) /* there's a name to be stored */ + { + identity_cache.name[identity] = name; + } + identity_cache.ident = identity; + PR_ASSERT(identity_cache.ident <= identity_cache.length); + PR_Unlock(identity_cache.ml); + + if (NULL != old) PR_DELETE(old); + if (NULL != names) PR_DELETE(names); + + return identity; +} /* PR_GetUniqueIdentity */ + +PR_IMPLEMENT(const char*) PR_GetNameForIdentity(PRDescIdentity ident) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (PR_TOP_IO_LAYER == ident) return NULL; + + PR_ASSERT(ident <= identity_cache.ident); + return (ident > identity_cache.ident) ? NULL : identity_cache.name[ident]; +} /* PR_GetNameForIdentity */ + +PR_IMPLEMENT(PRDescIdentity) PR_GetLayersIdentity(PRFileDesc* fd) +{ + PR_ASSERT(NULL != fd); + return fd->identity; +} /* PR_GetLayersIdentity */ + +PR_IMPLEMENT(PRFileDesc*) PR_GetIdentitiesLayer(PRFileDesc* fd, PRDescIdentity id) +{ + PRFileDesc *layer = fd; + + if (PR_TOP_IO_LAYER == id) return fd; + + for (layer = fd; layer != NULL; layer = layer->lower) + { + if (id == layer->identity) return layer; + } + for (layer = fd; layer != NULL; layer = layer->higher) + { + if (id == layer->identity) return layer; + } + return NULL; +} /* PR_GetIdentitiesLayer */ + +void _PR_InitLayerCache() +{ + memset(&identity_cache, 0, sizeof(identity_cache)); + identity_cache.ml = PR_NewLock(); + PR_ASSERT(NULL != identity_cache.ml); +} /* _PR_InitLayerCache */ + +/* prlayer.c */ diff --git a/pr/src/io/prlog.c b/pr/src/io/prlog.c new file mode 100644 index 00000000..cd1b6d4e --- /dev/null +++ b/pr/src/io/prlog.c @@ -0,0 +1,418 @@ +/* -*- 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 "prenv.h" +#include "prprf.h" +#include <string.h> + +/* + * Lock used to lock the log. + * + * We can't define _PR_LOCK_LOG simply as PR_Lock because PR_Lock may + * contain assertions. We have to avoid assertions in _PR_LOCK_LOG + * because PR_ASSERT calls PR_LogPrint, which in turn calls _PR_LOCK_LOG. + * This can lead to infinite recursion. + */ +static PRLock *_pr_logLock; +#if (defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY)) +#define _PR_LOCK_LOG() { _PR_LOCK_LOCK(_pr_logLock) +#define _PR_UNLOCK_LOG() _PR_LOCK_UNLOCK(_pr_logLock); } +#else + +#define _PR_LOCK_LOG() \ +{ \ + PRIntn _is; \ + PRThread *_me = _PR_MD_CURRENT_THREAD(); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSOFF(_is); \ + _PR_LOCK_LOCK(_pr_logLock) + +#define _PR_UNLOCK_LOG() \ + _PR_LOCK_UNLOCK(_pr_logLock); \ + PR_ASSERT(_me == _PR_MD_CURRENT_THREAD()); \ + if (!_PR_IS_NATIVE_THREAD(_me)) \ + _PR_INTSON(_is); \ +} + +#endif + +#ifdef XP_PC +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + +/* + * On NT, we can't define _PUT_LOG as PR_Write or _PR_MD_WRITE, + * because every asynchronous file io operation leads to a fiber context + * switch. So we define _PUT_LOG as fputs (from stdio.h). A side + * benefit is that fputs handles the LF->CRLF translation. This + * code can also be used on other platforms with file stream io. + */ +#if defined(WIN32) || defined(XP_OS2) +#define _PR_USE_STDIO_FOR_LOGGING +#endif + +/* Macros used to reduce #ifdef pollution */ + +#if defined(_PR_USE_STDIO_FOR_LOGGING) +#define _PUT_LOG(fd, buf, nb) fputs(buf, fd) +#elif defined(_PR_PTHREADS) +#define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb) +#elif defined(XP_MAC) +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE_SYNC(fd, buf, nb) +#else +#define _PUT_LOG(fd, buf, nb) _PR_MD_WRITE(fd, buf, nb) +#endif + +/************************************************************************/ + +static PRLogModuleInfo *logModules; + +#ifdef PR_LOGGING +static char *logBuf = NULL; +static char *logp; +static char *logEndp; +#ifdef _PR_USE_STDIO_FOR_LOGGING +static FILE *logFile = NULL; +#else +static PRFileDesc *logFile = 0; +#endif + +#define LINE_BUF_SIZE 200 +#define DEFAULT_BUF_SIZE 16384 + +#ifdef _PR_NEED_STRCASECMP + +/* + * strcasecmp is defined in /usr/ucblib/libucb.a on some platforms + * such as NCR and Unixware. Linking with both libc and libucb + * may cause some problem, so I just provide our own implementation + * of strcasecmp here. + */ + +static const unsigned char uc[] = +{ + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + ' ', '!', '"', '#', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ':', ';', '<', '=', '>', '?', + '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', + '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', + 'X', 'Y', 'Z', '{', '|', '}', '~', '\177' +}; + +PRIntn strcasecmp(const char *a, const char *b) +{ + const unsigned char *ua = (const unsigned char *)a; + const unsigned char *ub = (const unsigned char *)b; + + if( ((const char *)0 == a) || (const char *)0 == b ) + return (PRIntn)(a-b); + + while( (uc[*ua] == uc[*ub]) && ('\0' != *a) ) + { + a++; + ua++; + ub++; + } + + return (PRIntn)(uc[*ua] - uc[*ub]); +} + +#endif /* _PR_NEED_STRCASECMP */ + +void _PR_InitLog(void) +{ + char *ev; + + _pr_logLock = PR_NewLock(); + + ev = PR_GetEnv("NSPR_LOG_MODULES"); + if (ev && ev[0]) { + char module[64]; + PRBool isSync = PR_FALSE; + PRIntn evlen = strlen(ev), pos = 0; + PRInt32 bufSize = DEFAULT_BUF_SIZE; + while (pos < evlen) { + PRIntn level = 1, count = 0, delta = 0; + count = sscanf(&ev[pos], "%64[A-Za-z0-9]%n:%d%n", + module, &delta, &level, &delta); + pos += delta; + if (count == 0) break; + + /* + ** If count == 2, then we got module and level. If count + ** == 1, then level defaults to 1 (module enabled). + */ + if (strcasecmp(module, "sync") == 0) { + isSync = PR_TRUE; + } else if (strcasecmp(module, "bufsize") == 0) { + if (level >= LINE_BUF_SIZE) { + bufSize = level; + } + } else { + PRLogModuleInfo *lm = logModules; + PRBool skip_modcheck = + (0 == strcasecmp (module, "all")) ? PR_TRUE : PR_FALSE; + + while (lm != NULL) { + if (skip_modcheck) lm -> level = (PRLogModuleLevel)level; + else if (strcasecmp(module, lm->name) == 0) { + lm->level = (PRLogModuleLevel)level; + break; + } + lm = lm->next; + } + if (NULL == lm) { +#ifdef XP_PC + char* str = PR_smprintf("Unrecognized NSPR_LOG_MODULE: %s=%d\n", + module, level); + if (str) { + OutputDebugString(str); + PR_smprintf_free(str); + } +#else + fprintf(stderr, "Unrecognized NSPR_LOG_MODULE: %s=%d\n", + module, level); +#endif + } + } + /*found:*/ + count = sscanf(&ev[pos], " , %n", &delta); + pos += delta; + if (count == -1) break; + } + PR_SetLogBuffering(isSync ? bufSize : 0); + + ev = PR_GetEnv("NSPR_LOG_FILE"); + if (ev && ev[0]) { + if (!PR_SetLogFile(ev)) { +#ifdef XP_PC + char* str = PR_smprintf("Unable to create nspr log file '%s'\n", ev); + if (str) { + OutputDebugString(str); + PR_smprintf_free(str); + } +#else + fprintf(stderr, "Unable to create nspr log file '%s'\n", ev); +#endif + } + } else { +#ifdef _PR_USE_STDIO_FOR_LOGGING + logFile = stderr; +#else + logFile = _pr_stderr; +#endif + } + } +} + +void _PR_LogCleanup(void) +{ + PR_LogFlush(); + +#ifdef _PR_USE_STDIO_FOR_LOGGING + if (logFile && logFile != stdout && logFile != stderr) { + fclose(logFile); + } +#else + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } +#endif +} + +#endif /* PR_LOGGING */ + +PR_IMPLEMENT(PRLogModuleInfo*) PR_NewLogModule(const char *name) +{ + PRLogModuleInfo *lm; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + lm = PR_NEWZAP(PRLogModuleInfo); + if (lm) { + lm->name = strdup(name); + lm->level = PR_LOG_NONE; + lm->next = logModules; + logModules = lm; + } + return lm; +} + +PR_IMPLEMENT(PRBool) PR_SetLogFile(const char *file) +{ +#ifdef PR_LOGGING +#ifdef _PR_USE_STDIO_FOR_LOGGING + FILE *newLogFile; + + newLogFile = fopen(file, "w"); + if (newLogFile) { + /* We do buffering ourselves. */ + setvbuf(newLogFile, NULL, _IONBF, 0); + if (logFile && logFile != stdout && logFile != stderr) { + fclose(logFile); + } + logFile = newLogFile; + } + return (PRBool) (newLogFile != 0); +#else + PRFileDesc *newLogFile; + + newLogFile = PR_Open(file, PR_WRONLY|PR_CREATE_FILE, 0666); + if (newLogFile) { + if (logFile && logFile != _pr_stdout && logFile != _pr_stderr) { + PR_Close(logFile); + } + logFile = newLogFile; +#if defined(XP_MAC) + SetLogFileTypeCreator(file); +#endif + } + return (PRBool) (newLogFile != 0); +#endif /* _PR_USE_STDIO_FOR_LOGGING */ +#else /* PR_LOGGING */ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FALSE; +#endif /* PR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_SetLogBuffering(PRIntn buffer_size) +{ +#ifdef PR_LOGGING + PR_LogFlush(); + + if (logBuf) + PR_DELETE(logBuf); + logBuf = 0; + + if (buffer_size >= LINE_BUF_SIZE) { + logp = logBuf = (char*) PR_MALLOC(buffer_size); + logEndp = logp + buffer_size; + } +#endif /* PR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_LogPrint(const char *fmt, ...) +{ +#ifdef PR_LOGGING + va_list ap; + char line[LINE_BUF_SIZE]; + PRUint32 nb; + PRThread *me; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (!logFile) { + return; + } + + va_start(ap, fmt); + me = PR_GetCurrentThread(); + nb = PR_snprintf(line, sizeof(line)-1, "%ld[%p]: ", +#if defined(_PR_DCETHREADS) + /* The problem is that for _PR_DCETHREADS, pthread_t is not a + * pointer, but a structure; so you can't easily print it... + */ + me ? &(me->id): 0L, me); +#else + me ? me->id : 0L, me); +#endif + + nb += PR_vsnprintf(line+nb, sizeof(line)-nb-1, fmt, ap); + if (nb && (line[nb-1] != '\n')) { +#ifndef XP_MAC + line[nb++] = '\n'; +#else + line[nb++] = '\015'; +#endif + line[nb] = '\0'; + } else { +#ifdef XP_MAC + line[nb-1] = '\015'; +#endif + } + va_end(ap); + + _PR_LOCK_LOG(); + if (logBuf == 0) { + _PUT_LOG(logFile, line, nb); + } else { + if (logp + nb > logEndp) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + memcpy(logp, line, nb); + logp += nb; + } + _PR_UNLOCK_LOG(); + PR_LogFlush(); +#endif /* PR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_LogFlush(void) +{ +#ifdef PR_LOGGING + if (logBuf && logFile) { + _PR_LOCK_LOG(); + if (logp > logBuf) { + _PUT_LOG(logFile, logBuf, logp - logBuf); + logp = logBuf; + } + _PR_UNLOCK_LOG(); + } +#endif /* PR_LOGGING */ +} + +PR_IMPLEMENT(void) PR_Abort(void) +{ +#ifdef PR_LOGGING + PR_LogPrint("Aborting"); + abort(); +#endif /* PR_LOGGING */ + PR_ASSERT(1); +} + +PR_IMPLEMENT(void) PR_Assert(const char *s, const char *file, PRIntn ln) +{ +#ifdef PR_LOGGING + PR_LogPrint("Assertion failure: %s, at %s:%d\n", s, file, ln); +#if defined(XP_UNIX) || defined(XP_OS2) + fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); +#endif +#ifdef XP_MAC + dprintf("Assertion failure: %s, at %s:%d\n", s, file, ln); +#endif +#ifdef WIN32 + DebugBreak(); +#endif +#ifndef XP_MAC + abort(); +#endif +#endif /* PR_LOGGING */ +} diff --git a/pr/src/io/prmapopt.c b/pr/src/io/prmapopt.c new file mode 100644 index 00000000..c9ed829a --- /dev/null +++ b/pr/src/io/prmapopt.c @@ -0,0 +1,408 @@ +/* -*- 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 defines _PR_MapOptionName(). The purpose of putting + * _PR_MapOptionName() in a separate file is to work around a Winsock + * header file problem on Windows NT. + * + * On Windows NT, if we define _WIN32_WINNT to be 0x0400 (in order + * to use Service Pack 3 extensions), windows.h includes winsock2.h + * (instead of winsock.h), which doesn't define many socket options + * defined in winsock.h. + * + * We need the socket options defined in winsock.h. So this file + * includes winsock.h, with _WIN32_WINNT undefined. + */ + +#ifdef WINNT +#include <winsock.h> +#endif + +#include "primpl.h" + +#if defined(XP_UNIX) || defined(OS2) +#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#endif + +/* + * Not every platform has all the socket options we want to + * support. Some older operating systems such as SunOS 4.1.3 + * don't have the IP multicast socket options. Win32 doesn't + * have TCP_MAXSEG. + * + * To deal with this problem, we define the missing socket + * options as _PR_NO_SUCH_SOCKOPT. _PR_MapOptionName() fails with + * PR_OPERATION_NOT_SUPPORTED_ERROR if a socket option not + * available on the platform is requested. + */ + +/* + * Sanity check. SO_LINGER and TCP_NODELAY should be available + * on all platforms. Just to make sure we have included the + * appropriate header files. Then any undefined socket options + * are really missing. + */ + +#if !defined(SO_LINGER) +#error "SO_LINGER is not defined" +#endif + +#if !defined(TCP_NODELAY) +#error "TCP_NODELAY is not defined" +#endif + +/* + * Make sure the value of _PR_NO_SUCH_SOCKOPT is not + * a valid socket option. + */ +#define _PR_NO_SUCH_SOCKOPT -1 + +#ifndef IP_MULTICAST_IF /* set/get IP multicast interface */ +#define IP_MULTICAST_IF _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_MULTICAST_TTL /* set/get IP multicast timetolive */ +#define IP_MULTICAST_TTL _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_MULTICAST_LOOP /* set/get IP multicast loopback */ +#define IP_MULTICAST_LOOP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_ADD_MEMBERSHIP /* add an IP group membership */ +#define IP_ADD_MEMBERSHIP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_DROP_MEMBERSHIP /* drop an IP group membership */ +#define IP_DROP_MEMBERSHIP _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_TTL /* set/get IP Time To Live */ +#define IP_TTL _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef IP_TOS /* set/get IP Type Of Service */ +#define IP_TOS _PR_NO_SUCH_SOCKOPT +#endif + +#ifndef TCP_MAXSEG /* maxumum segment size for tcp */ +#define TCP_MAXSEG _PR_NO_SUCH_SOCKOPT +#endif + +PRStatus _PR_MapOptionName( + PRSockOption optname, PRInt32 *level, PRInt32 *name) +{ + static PRInt32 socketOptions[PR_SockOpt_Last] = + { + 0, SO_LINGER, SO_REUSEADDR, SO_KEEPALIVE, SO_RCVBUF, SO_SNDBUF, + IP_TTL, IP_TOS, IP_ADD_MEMBERSHIP, IP_DROP_MEMBERSHIP, + IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP, + TCP_NODELAY, TCP_MAXSEG + }; + static PRInt32 socketLevels[PR_SockOpt_Last] = + { + 0, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, SOL_SOCKET, + IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, + IPPROTO_IP, IPPROTO_IP, IPPROTO_IP, + IPPROTO_TCP, IPPROTO_TCP + }; + + if ((optname < PR_SockOpt_Linger) + && (optname > PR_SockOpt_MaxSegment)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + if (socketOptions[optname] == _PR_NO_SUCH_SOCKOPT) + { + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; + } + *name = socketOptions[optname]; + *level = socketLevels[optname]; + return PR_SUCCESS; +} /* _PR_MapOptionName */ + +#ifndef _PR_PTHREADS + +PRStatus PR_CALLBACK _PR_SocketGetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + PRStatus rv; + PRInt32 length; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == data->option) + { + data->value.non_blocking = fd->secret->nonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + length = sizeof(linger); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char *) &linger, &length); + if (PR_SUCCESS == rv) + { + PR_ASSERT(sizeof(linger) == length); + data->value.linger.polarity = + (linger.l_onoff) ? PR_TRUE : PR_FALSE; + data->value.linger.linger = + PR_SecondsToInterval(linger.l_linger); + } + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + { +#ifdef WIN32 /* Winsock */ + BOOL value; +#else + PRIntn value; +#endif + length = sizeof(value); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&value, &length); + if (PR_SUCCESS == rv) + data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_McastLoopback: + { +#ifdef WIN32 /* Winsock */ + BOOL bool; +#else + PRUint8 bool; +#endif + length = sizeof(bool); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&bool, &length); + if (PR_SUCCESS == rv) + data->value.mcast_loopback = (0 == bool) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value; + length = sizeof(value); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&value, &length); + if (PR_SUCCESS == rv) + data->value.recv_buffer_size = value; + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + /* These options should really be an int (or PRIntn). */ + length = sizeof(PRUintn); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&data->value.ip_ttl, &length); + break; + } + case PR_SockOpt_McastTimeToLive: + { +#ifdef WIN32 /* Winsock */ + int ttl; +#else + PRUint8 ttl; +#endif + length = sizeof(ttl); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&ttl, &length); + if (PR_SUCCESS == rv) + data->value.mcast_ttl = ttl; + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + length = sizeof(mreq); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char*)&mreq, &length); + if (PR_SUCCESS == rv) + { + data->value.add_member.mcaddr.inet.ip = + mreq.imr_multiaddr.s_addr; + data->value.add_member.ifaddr.inet.ip = + mreq.imr_interface.s_addr; + } + break; + } + case PR_SockOpt_McastInterface: + { + /* This option is a struct in_addr. */ + length = sizeof(data->value.mcast_if.inet.ip); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, + (char*)&data->value.mcast_if.inet.ip, &length); + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + } + return rv; +} /* _PR_SocketGetSocketOption */ + +PRStatus PR_CALLBACK _PR_SocketSetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) +{ + PRStatus rv; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == data->option) + { +#ifdef WINNT + PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) + || (fd->secret->nonblocking == data->value.non_blocking)); + if (fd->secret->md.io_model_committed + && (fd->secret->nonblocking != data->value.non_blocking)) + { + /* + * On NT, once we have associated a socket with the io + * completion port, we can't disassociate it. So we + * can't change the nonblocking option of the socket + * afterwards. + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } +#endif + fd->secret->nonblocking = data->value.non_blocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + linger.l_onoff = data->value.linger.polarity; + linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&linger, sizeof(linger)); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + { +#ifdef WIN32 /* Winsock */ + BOOL value; +#else + PRIntn value; +#endif + value = (data->value.reuse_addr) ? 1 : 0; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&value, sizeof(value)); + break; + } + case PR_SockOpt_McastLoopback: + { +#ifdef WIN32 /* Winsock */ + BOOL bool; +#else + PRUint8 bool; +#endif + bool = data->value.mcast_loopback ? 1 : 0; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&bool, sizeof(bool)); + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value = data->value.recv_buffer_size; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&value, sizeof(value)); + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + /* These options should really be an int (or PRIntn). */ + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&data->value.ip_ttl, sizeof(PRUintn)); + break; + } + case PR_SockOpt_McastTimeToLive: + { +#ifdef WIN32 /* Winsock */ + int ttl; +#else + PRUint8 ttl; +#endif + ttl = data->value.mcast_ttl; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&ttl, sizeof(ttl)); + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = + data->value.add_member.mcaddr.inet.ip; + mreq.imr_interface.s_addr = + data->value.add_member.ifaddr.inet.ip; + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&mreq, sizeof(mreq)); + break; + } + case PR_SockOpt_McastInterface: + { + /* This option is a struct in_addr. */ + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char*)&data->value.mcast_if.inet.ip, + sizeof(data->value.mcast_if.inet.ip)); + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + } + return rv; +} /* _PR_SocketSetSocketOption */ + +#endif /* ! _PR_PTHREADS */ diff --git a/pr/src/io/prmmap.c b/pr/src/io/prmmap.c new file mode 100644 index 00000000..f5f66465 --- /dev/null +++ b/pr/src/io/prmmap.c @@ -0,0 +1,69 @@ +/* -*- 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. + */ + +/* + ********************************************************************* + * + * Memory-mapped files + * + ********************************************************************* + */ + +#include "primpl.h" + +PR_IMPLEMENT(PRFileMap *) PR_CreateFileMap( + PRFileDesc *fd, + PRInt64 size, + PRFileMapProtect prot) +{ + PRFileMap *fmap; + + PR_ASSERT(prot == PR_PROT_READONLY || prot == PR_PROT_READWRITE + || prot == PR_PROT_WRITECOPY); + fmap = PR_NEWZAP(PRFileMap); + if (NULL == fmap) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + fmap->fd = fd; + fmap->prot = prot; + if (_PR_MD_CREATE_FILE_MAP(fmap, size) == PR_SUCCESS) { + return fmap; + } else { + PR_DELETE(fmap); + return NULL; + } +} + +PR_IMPLEMENT(void *) PR_MemMap( + PRFileMap *fmap, + PRInt64 offset, + PRUint32 len) +{ + return _PR_MD_MEM_MAP(fmap, offset, len); +} + +PR_IMPLEMENT(PRStatus) PR_MemUnmap(void *addr, PRUint32 len) +{ + return _PR_MD_MEM_UNMAP(addr, len); +} + +PR_IMPLEMENT(PRStatus) PR_CloseFileMap(PRFileMap *fmap) +{ + return _PR_MD_CLOSE_FILE_MAP(fmap); +} diff --git a/pr/src/io/prmwait.c b/pr/src/io/prmwait.c new file mode 100644 index 00000000..c29628ea --- /dev/null +++ b/pr/src/io/prmwait.c @@ -0,0 +1,702 @@ +/* -*- 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 "prlog.h" +#include "prmem.h" +#include "primpl.h" +#include "prmwait.h" +#include "prerror.h" +#include "pprmwait.h" + +static PRLock *mw_lock = NULL; +static _PRGlobalState *mw_state = NULL; + +static PRIntervalTime max_polling_interval; + +/******************************************************************/ +/******************************************************************/ +/************************ The private portion *********************/ +/******************************************************************/ +/******************************************************************/ +static PRStatus MW_Init(void) +{ + if (NULL != mw_lock) return PR_SUCCESS; + if (NULL != (mw_lock = PR_NewLock())) + { + _PRGlobalState *state = PR_NEWZAP(_PRGlobalState); + if (state == NULL) goto failed; + + PR_INIT_CLIST(&state->group_list); + + PR_Lock(mw_lock); + if (NULL == mw_state) /* is it still NULL? */ + { + mw_state = state; /* if yes, set our value */ + state = NULL; /* and indicate we've done the job */ + max_polling_interval = PR_MillisecondsToInterval(MAX_POLLING_INTERVAL); + } + PR_Unlock(mw_lock); + if (NULL != state) PR_DELETE(state); + return PR_SUCCESS; + } + +failed: + return PR_FAILURE; +} /* MW_Init */ + +static PRWaitGroup *MW_Init2(void) +{ + PRWaitGroup *group = mw_state->group; /* it's the null group */ + if (NULL == group) /* there is this special case */ + { + group = PR_CreateWaitGroup(_PR_DEFAULT_HASH_LENGTH); + if (NULL == group) goto failed_alloc; + PR_Lock(mw_lock); + if (NULL == mw_state->group) + { + mw_state->group = group; + group = NULL; + } + PR_Unlock(mw_lock); + if (group != NULL) (void)PR_DestroyWaitGroup(group); + group = mw_state->group; /* somebody beat us to it */ + } +failed_alloc: + return group; /* whatever */ +} /* MW_Init2 */ + +static _PR_HashStory MW_AddHashInternal(PRRecvWait *desc, _PRWaiterHash *hash) +{ + /* + ** The entries are put in the table using the fd (PRFileDesc*) of + ** the receive descriptor as the key. This allows us to locate + ** the appropriate entry aqain when the poll operation finishes. + ** + ** The pointer to the file descriptor object is first divided by + ** the natural alignment of a pointer in the belief that object + ** will have at least that many zeros in the low order bits. + ** This may not be a good assuption. + ** + ** We try to put the entry in by rehashing three times. After + ** that we declare defeat and force the table to be reconstructed. + ** Since some fds might be added more than once, won't that cause + ** collisions even in an empty table? + */ + PRIntn rehash = 11; + PRRecvWait **waiter; + PRUintn hidx = _MW_HASH(desc->fd, hash->length); + do + { + waiter = &hash->recv_wait; + if (NULL == waiter[hidx]) + { + waiter[hidx] = desc; + hash->count += 1; +#if 0 + printf("Adding 0x%x->0x%x ", desc, desc->fd); + printf( + "table[%u:%u:*%u]: 0x%x->0x%x\n", + hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd); +#endif + return _prmw_success; + } + if (desc == waiter[hidx]) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); /* desc already in table */ + return _prmw_error; + } +#if 0 + printf("Failing 0x%x->0x%x ", desc, desc->fd); + printf( + "table[*%u:%u:%u]: 0x%x->0x%x\n", + hidx, hash->count, hash->length, waiter[hidx], waiter[hidx]->fd); +#endif + hidx = _MW_REHASH(desc->fd, hidx, hash->length); + } while (--rehash > 0); + return _prmw_rehash; +} /* MW_AddHashInternal */ + +static _PR_HashStory MW_ExpandHashInternal(PRWaitGroup *group) +{ + PRRecvWait **desc; + PRUint32 pidx, length = 0; + _PRWaiterHash *newHash, *oldHash = group->waiter; + + + static const PRInt32 prime_number[] = { + _PR_DEFAULT_HASH_LENGTH, 179, 521, 907, 1427, + 2711, 3917, 5021, 8219, 11549, 18911, 26711, 33749, 44771}; + PRUintn primes = (sizeof(prime_number) / sizeof(PRIntn)); + + /* look up the next size we'd like to use for the hash table */ + for (pidx = 0; pidx < primes; ++pidx) + { + if (prime_number[pidx] == oldHash->length) + { + length = prime_number[pidx + 1]; + break; + } + } + if (0 == length) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return _prmw_error; /* we're hosed */ + } + + /* allocate the new hash table and fill it in with the old */ + newHash = (_PRWaiterHash*)PR_CALLOC( + sizeof(_PRWaiterHash) + (length * sizeof(PRRecvWait*))); + + newHash->length = length; + for (desc = &oldHash->recv_wait; newHash->count < oldHash->count; ++desc) + { + if (NULL != *desc) + { + if (_prmw_success != MW_AddHashInternal(*desc, newHash)) + { + PR_ASSERT(!"But, but, but ..."); + PR_DELETE(newHash); + return _prmw_error; + } + } + } + PR_DELETE(group->waiter); + group->waiter = newHash; + return _prmw_success; +} /* MW_ExpandHashInternal */ + +static void _MW_DoneInternal( + PRWaitGroup *group, PRRecvWait **waiter, PRMWStatus outcome) +{ + /* + ** Add this receive wait object to the list of finished I/O + ** operations for this particular group. If there are other + ** threads waiting on the group, notify one. If not, arrange + ** for this thread to return. + */ + +#if 0 + printf("Removing 0x%x->0x%x\n", *waiter, (*waiter)->fd); +#endif + (*waiter)->outcome = outcome; + PR_APPEND_LINK(&((*waiter)->internal), &group->io_ready); + PR_NotifyCondVar(group->io_complete); + PR_ASSERT(0 != group->waiter->count); + group->waiter->count -= 1; + *waiter = NULL; +} /* _MW_DoneInternal */ + +static PRRecvWait **_MW_LookupInternal(PRWaitGroup *group, PRFileDesc *fd) +{ + /* + ** Find the receive wait object corresponding to the file descriptor. + ** Only search the wait group specified. + */ + PRRecvWait **desc; + PRIntn rehash = 11; + _PRWaiterHash *hash = group->waiter; + PRUintn hidx = _MW_HASH(fd, hash->length); + + while (rehash-- > 0) + { + desc = (&hash->recv_wait) + hidx; + if ((*desc != NULL) && ((*desc)->fd == fd)) return desc; + hidx = _MW_REHASH(fd, hidx, hash->length); + } + return NULL; +} /* _MW_LookupInternal */ + +static PRStatus _MW_PollInternal(PRWaitGroup *group) +{ + PRRecvWait **waiter; + PRStatus rv = PR_FAILURE; + PRUintn count, count_ready; + PRIntervalTime polling_interval; + + group->poller = PR_GetCurrentThread(); + + PR_Unlock(group->ml); + + while (PR_TRUE) + { + PRIntervalTime now, since_last_poll; + PRPollDesc *poll_list = group->polling_list; + /* + ** There's something to do. See if our existing polling list + ** is large enough for what we have to do? + */ + + while (group->polling_count < group->waiter->count) + { + PRUint32 old_count = group->waiter->count; + PRUint32 new_count = PR_ROUNDUP(old_count, _PR_POLL_COUNT_FUDGE); + PRSize new_size = sizeof(PRPollDesc) * new_count; + poll_list = (PRPollDesc*)PR_CALLOC(new_size); + if (NULL == poll_list) goto failed_alloc; + if (NULL != group->polling_list) + PR_DELETE(group->polling_list); + group->polling_list = poll_list; + group->polling_count = new_count; + } + + now = PR_IntervalNow(); + polling_interval = max_polling_interval; + since_last_poll = now - group->last_poll; + PR_Lock(group->ml); + waiter = &group->waiter->recv_wait; + + for (count = 0; count < group->waiter->count; ++waiter) + { + if (NULL != *waiter) /* a live one! */ + { + if (since_last_poll >= (*waiter)->timeout) + _MW_DoneInternal(group, waiter, PR_MW_TIMEOUT); + else + { + if (PR_INTERVAL_NO_TIMEOUT != (*waiter)->timeout) + { + (*waiter)->timeout -= since_last_poll; + if ((*waiter)->timeout < polling_interval) + polling_interval = (*waiter)->timeout; + } + poll_list->fd = (*waiter)->fd; + poll_list->in_flags = PR_POLL_READ; + poll_list->out_flags = 0; +#if 0 + printf( + "Polling 0x%x[%d]: [fd: 0x%x, tmo: %u]\n", + poll_list, count, poll_list->fd, (*waiter)->timeout); +#endif + poll_list += 1; + count += 1; + } + } + } + + PR_ASSERT(count == group->waiter->count); + if (0 == count) break; + + group->last_poll = now; + + PR_Unlock(group->ml); + + count_ready = PR_Poll(group->polling_list, count, polling_interval); + + PR_Lock(group->ml); + + if (-1 == count_ready) goto failed_poll; /* that's a shame */ + for (poll_list = group->polling_list; count > 0; poll_list++, count--) + { + if (poll_list->out_flags != 0) + { + waiter = _MW_LookupInternal(group, poll_list->fd); + if (NULL != waiter) + _MW_DoneInternal(group, waiter, PR_MW_SUCCESS); + } + } + /* + ** If there are no more threads waiting for completion, + ** we need to return. + ** This thread was "borrowed" to do the polling, but it really + ** belongs to the client. + */ + if ((_prmw_running != group->state) + || (0 == group->waiting_threads)) break; + PR_Unlock(group->ml); + } + + rv = PR_SUCCESS; + +failed_poll: +failed_alloc: + group->poller = NULL; /* we were that, not we ain't */ + return rv; /* we return with the lock held */ +} /* _MW_PollInternal */ + +static PRMWGroupState MW_TestForShutdownInternal(PRWaitGroup *group) +{ + PRMWGroupState rv = group->state; + /* + ** Looking at the group's fields is safe because + ** once the group's state is no longer running, it + ** cannot revert and there is a safe check on entry + ** to make sure no more threads are made to wait. + */ + if ((_prmw_stopping == rv) + && (0 == group->waiting_threads) + && PR_CLIST_IS_EMPTY(&group->io_ready) + && (0 == group->waiter->count)) + { + rv = group->state = _prmw_stopped; + PR_NotifyCondVar(group->mw_manage); + } + return rv; +} /* MW_TestForShutdownInternal */ + +static void _MW_InitialRecv(PRCList *io_ready) +{ + PRRecvWait *desc = (PRRecvWait*)io_ready; + if ((NULL == desc->buffer.start) + || (0 == desc->buffer.length)) + desc->bytesRecv = 0; + else + { + desc->bytesRecv = desc->fd->methods->recv( + desc->fd, desc->buffer.start, + desc->buffer.length, 0, desc->timeout); + if (desc->bytesRecv < 0) /* SetError should already be there */ + desc->outcome = PR_MW_FAILURE; + } +} /* _MW_InitialRecv */ + +/******************************************************************/ +/******************************************************************/ +/********************** The public API portion ********************/ +/******************************************************************/ +/******************************************************************/ +PR_IMPLEMENT(PRStatus) PR_AddWaitFileDesc( + PRWaitGroup *group, PRRecvWait *desc) +{ + _PR_HashStory hrv; + PRStatus rv = PR_FAILURE; + if (PR_FAILURE == MW_Init()) goto failed_init; + if ((NULL == group) && (NULL == (group = MW_Init2()))) goto failed_init; + + PR_ASSERT(NULL != desc->fd); + + desc->outcome = PR_MW_PENDING; /* nice, well known value */ + desc->bytesRecv = 0; /* likewise, though this value is ambiguious */ + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + /* Not allowed to add after cancelling the group */ + desc->outcome = PR_MW_INTERRUPT; + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto invalid_state; + } + + /* + ** If the waiter count is zero at this point, there's no telling + ** how long we've been idle. Therefore, initialize the beginning + ** of the timing interval. As long as the list doesn't go empty, + ** it will maintain itself. + */ + if (0 == group->waiter->count) + group->last_poll = PR_IntervalNow(); + do + { + hrv = MW_AddHashInternal(desc, group->waiter); + if (_prmw_rehash != hrv) break; + hrv = MW_ExpandHashInternal(group); /* gruesome */ + if (_prmw_success != hrv) break; + } while (PR_TRUE); + + PR_NotifyCondVar(group->new_business); /* tell the world */ + rv = (_prmw_success == hrv) ? PR_SUCCESS : PR_FAILURE; + +failed_init: +invalid_state: + PR_Unlock(group->ml); + return rv; +} /* PR_AddWaitFileDesc */ + +PR_IMPLEMENT(PRRecvWait*) PR_WaitRecvReady(PRWaitGroup *group) +{ + PRStatus rv = PR_SUCCESS; + PRCList *io_ready = NULL; + if (PR_FAILURE == MW_Init()) goto failed_init; + if ((NULL == group) && (NULL == (group = MW_Init2()))) goto failed_init; + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + goto invalid_state; + } + + group->waiting_threads += 1; /* the polling thread is counted */ + + do + { + /* + ** If the I/O ready list isn't empty, have this thread + ** return with the first receive wait object that's available. + */ + if (PR_CLIST_IS_EMPTY(&group->io_ready)) + { + while ((NULL == group->waiter) || (0 == group->waiter->count)) + { + if (_prmw_running != group->state) goto aborted; + rv = PR_WaitCondVar(group->new_business, PR_INTERVAL_NO_TIMEOUT); + if (_MW_ABORTED(rv)) goto aborted; + } + + /* + ** Is there a polling thread yet? If not, grab this thread + ** and use it. + */ + if (NULL == group->poller) + { + /* + ** This thread will stay do polling until it becomes the only one + ** left to service a completion. Then it will return and there will + ** be none left to actually poll or to run completions. + ** + ** The polling function should only return w/ failure or + ** with some I/O ready. + */ + if (PR_FAILURE == _MW_PollInternal(group)) goto failed_poll; + if (PR_CLIST_IS_EMPTY(&group->io_ready)) continue; /* timeout */ + } + else + { + while (PR_CLIST_IS_EMPTY(&group->io_ready)) + { + rv = PR_WaitCondVar(group->io_complete, PR_INTERVAL_NO_TIMEOUT); + if (_MW_ABORTED(rv)) goto aborted; + } + } + } + io_ready = PR_LIST_HEAD(&group->io_ready); + PR_NotifyCondVar(group->io_taken); + PR_ASSERT(io_ready != NULL); + PR_REMOVE_LINK(io_ready); + + /* If the operation failed, record the reason why */ + switch (((PRRecvWait*)io_ready)->outcome) + { + case PR_MW_PENDING: + PR_ASSERT(PR_MW_PENDING != ((PRRecvWait*)io_ready)->outcome); + break; + case PR_MW_SUCCESS: + _MW_InitialRecv(io_ready); + break; + case PR_MW_TIMEOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_MW_INTERRUPT: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + break; + default: break; + } + } while (NULL == io_ready); + +aborted: +failed_poll: + group->waiting_threads -= 1; +invalid_state: + (void)MW_TestForShutdownInternal(group); + PR_Unlock(group->ml); + +failed_init: + + return (PRRecvWait*)io_ready; +} /* PR_WaitRecvReady */ + +PR_IMPLEMENT(PRStatus) PR_CancelWaitFileDesc(PRWaitGroup *group, PRRecvWait *desc) +{ + PRRecvWait **recv_wait; + PRStatus rv = PR_SUCCESS; + if (PR_FAILURE == MW_Init()) return rv; + if (NULL == group) group = mw_state->group; + PR_ASSERT(NULL != group); + if (NULL == group) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + PR_Lock(group->ml); + + if (_prmw_running != group->state) + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + rv = PR_FAILURE; + goto stopping; + } + + if (NULL != (recv_wait = _MW_LookupInternal(group, desc->fd))) + { + /* it was in the wait table */ + _MW_DoneInternal(group, recv_wait, PR_MW_INTERRUPT); + goto found; + } + if (!PR_CLIST_IS_EMPTY(&group->io_ready)) + { + /* is it already complete? */ + PRCList *head = PR_LIST_HEAD(&group->io_ready); + do + { + PRRecvWait *done = (PRRecvWait*)head; + if (done == desc) goto found; + head = PR_NEXT_LINK(head); + } while (head != &group->io_ready); + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + +found: +stopping: + PR_Unlock(group->ml); + return rv; +} /* PR_CancelWaitFileDesc */ + +PR_IMPLEMENT(PRRecvWait*) PR_CancelWaitGroup(PRWaitGroup *group) +{ + PRRecvWait **desc; + PRRecvWait *recv_wait = NULL; + if (PR_FAILURE == MW_Init()) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + if (NULL == group) group = mw_state->group; + PR_ASSERT(NULL != group); + if (NULL == group) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + + PR_Lock(group->ml); + if (_prmw_stopped != group->state) + { + if (_prmw_running == group->state) + group->state = _prmw_stopping; /* so nothing new comes in */ + if (0 == group->waiting_threads) /* is there anybody else? */ + group->state = _prmw_stopped; /* we can stop right now */ + while (_prmw_stopped != group->state) + (void)PR_WaitCondVar(group->mw_manage, PR_INTERVAL_NO_TIMEOUT); + + /* make all the existing descriptors look done/interrupted */ + for (desc = &group->waiter->recv_wait; group->waiter->count > 0; ++desc) + { + if (NULL != *desc) + _MW_DoneInternal(group, desc, PR_MW_INTERRUPT); + } + + PR_NotifyAllCondVar(group->new_business); + } + + /* take first element of finished list and return it or NULL */ + if (PR_CLIST_IS_EMPTY(&group->io_ready)) + PR_SetError(PR_GROUP_EMPTY_ERROR, 0); + else + { + PRCList *head = PR_LIST_HEAD(&group->io_ready); + PR_REMOVE_AND_INIT_LINK(head); + recv_wait = (PRRecvWait*)head; + } + PR_Unlock(group->ml); + + return recv_wait; +} /* PR_CancelWaitGroup */ + +PR_IMPLEMENT(PRWaitGroup*) PR_CreateWaitGroup(PRInt32 size /* ignored */) +{ + PRWaitGroup *wg = NULL; + if (PR_FAILURE == MW_Init()) goto failed; + + if (NULL == (wg = PR_NEWZAP(PRWaitGroup))) goto failed; + /* the wait group itself */ + wg->ml = PR_NewLock(); + if (NULL == wg->ml) goto failed_lock; + wg->io_taken = PR_NewCondVar(wg->ml); + if (NULL == wg->io_taken) goto failed_cvar0; + wg->io_complete = PR_NewCondVar(wg->ml); + if (NULL == wg->io_complete) goto failed_cvar1; + wg->new_business = PR_NewCondVar(wg->ml); + if (NULL == wg->new_business) goto failed_cvar2; + wg->mw_manage = PR_NewCondVar(wg->ml); + if (NULL == wg->mw_manage) goto failed_cvar3; + + PR_INIT_CLIST(&wg->group_link); + PR_INIT_CLIST(&wg->io_ready); + + /* the waiters sequence */ + wg->waiter = (_PRWaiterHash*)PR_CALLOC( + sizeof(_PRWaiterHash) + + (_PR_DEFAULT_HASH_LENGTH * sizeof(PRRecvWait*))); + if (NULL == wg->waiter) goto failed_waiter; + wg->waiter->count = 0; + wg->waiter->length = _PR_DEFAULT_HASH_LENGTH; + + PR_Lock(mw_lock); + PR_APPEND_LINK(&wg->group_link, &mw_state->group_list); + PR_Unlock(mw_lock); + return wg; + +failed_waiter: + PR_DestroyCondVar(wg->mw_manage); +failed_cvar3: + PR_DestroyCondVar(wg->new_business); +failed_cvar2: + PR_DestroyCondVar(wg->io_taken); +failed_cvar1: + PR_DestroyCondVar(wg->io_complete); +failed_cvar0: + PR_DestroyLock(wg->ml); +failed_lock: + PR_DELETE(wg); + +failed: + return wg; +} /* MW_CreateWaitGroup */ + +PR_IMPLEMENT(PRStatus) PR_DestroyWaitGroup(PRWaitGroup *group) +{ + PRStatus rv = PR_SUCCESS; + if (NULL == group) group = mw_state->group; + PR_ASSERT(NULL != group); + if (NULL != group) + { + if (_prmw_stopped != group->state) /* quick, unsafe test */ + { + PRMWGroupState mws; + /* One shot to correct the situation */ + PR_Lock(group->ml); + if (group->state < _prmw_stopped) /* safer test */ + group->state = _prmw_stopping; + mws = MW_TestForShutdownInternal(group); + PR_Unlock(group->ml); + if (_prmw_stopped != mws) /* quick test again */ + { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + } + + PR_Lock(mw_lock); + PR_REMOVE_LINK(&group->group_link); + PR_Unlock(mw_lock); + + PR_DELETE(group->waiter); + PR_DestroyCondVar(group->new_business); + PR_DestroyCondVar(group->io_complete); + PR_DestroyCondVar(group->io_taken); + PR_DestroyLock(group->ml); + if (group == mw_state->group) mw_state->group = NULL; + PR_DELETE(group); + } + return rv; +} /* PR_DestroyWaitGroup */ + +/* prmwait.c */ diff --git a/pr/src/io/prprf.c b/pr/src/io/prprf.c new file mode 100644 index 00000000..ce8d5330 --- /dev/null +++ b/pr/src/io/prprf.c @@ -0,0 +1,1226 @@ +/* -*- 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. + */ + +/* +** Portable safe sprintf code. +** +** Author: Kipp E.B. Hickman +*/ +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include "primpl.h" +#include "prprf.h" +#include "prlong.h" +#include "prlog.h" +#include "prmem.h" + +/* +** Note: on some platforms va_list is defined as an array, +** and requires array notation. +*/ +#if defined(MACLINUX) || defined(WIN16) +#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] +#else +#define VARARGS_ASSIGN(foo, bar) (foo) = (bar) +#endif /*MACLINUX*/ + +/* +** WARNING: This code may *NOT* call PR_LOG (because PR_LOG calls it) +*/ + +/* +** XXX This needs to be internationalized! +*/ + +typedef struct SprintfStateStr SprintfState; + +struct SprintfStateStr { + int (*stuff)(SprintfState *ss, const char *sp, PRUint32 len); + + char *base; + char *cur; + PRUint32 maxlen; + + int (*func)(void *arg, const char *sp, PRUint32 len); + void *arg; +}; + +/* +** Numbered Arguement State +*/ +struct NumArgState{ + int type; /* type of the current ap */ + va_list ap; /* point to the corresponding position on ap */ +}; + +static PRBool l10n_debug_init = PR_FALSE; +static PRBool l10n_debug = PR_FALSE; + +#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ + + +#define TYPE_INT16 0 +#define TYPE_UINT16 1 +#define TYPE_INTN 2 +#define TYPE_UINTN 3 +#define TYPE_INT32 4 +#define TYPE_UINT32 5 +#define TYPE_INT64 6 +#define TYPE_UINT64 7 +#define TYPE_STRING 8 +#define TYPE_DOUBLE 9 +#define TYPE_INTSTR 10 +#define TYPE_UNKNOWN 20 + +#define _LEFT 0x1 +#define _SIGNED 0x2 +#define _SPACED 0x4 +#define _ZEROS 0x8 +#define _NEG 0x10 + +/* +** Fill into the buffer using the data in src +*/ +static int fill2(SprintfState *ss, const char *src, int srclen, int width, + int flags) +{ + char space = ' '; + int rv; + + width -= srclen; + if ((width > 0) && ((flags & _LEFT) == 0)) { /* Right adjusting */ + if (flags & _ZEROS) { + space = '0'; + } + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Copy out the source data */ + rv = (*ss->stuff)(ss, src, srclen); + if (rv < 0) { + return rv; + } + + if ((width > 0) && ((flags & _LEFT) != 0)) { /* Left adjusting */ + while (--width >= 0) { + rv = (*ss->stuff)(ss, &space, 1); + if (rv < 0) { + return rv; + } + } + } + return 0; +} + +/* +** Fill a number. The order is: optional-sign zero-filling conversion-digits +*/ +static int fill_n(SprintfState *ss, const char *src, int srclen, int width, + int prec, int type, int flags) +{ + int zerowidth = 0; + int precwidth = 0; + int signwidth = 0; + int leftspaces = 0; + int rightspaces = 0; + int cvtwidth; + int rv; + char sign; + + if ((type & 1) == 0) { + if (flags & _NEG) { + sign = '-'; + signwidth = 1; + } else if (flags & _SIGNED) { + sign = '+'; + signwidth = 1; + } else if (flags & _SPACED) { + sign = ' '; + signwidth = 1; + } + } + cvtwidth = signwidth + srclen; + + if (prec > 0) { + if (prec > srclen) { + precwidth = prec - srclen; /* Need zero filling */ + cvtwidth += precwidth; + } + } + + if ((flags & _ZEROS) && (prec < 0)) { + if (width > cvtwidth) { + zerowidth = width - cvtwidth; /* Zero filling */ + cvtwidth += zerowidth; + } + } + + if (flags & _LEFT) { + if (width > cvtwidth) { + /* Space filling on the right (i.e. left adjusting) */ + rightspaces = width - cvtwidth; + } + } else { + if (width > cvtwidth) { + /* Space filling on the left (i.e. right adjusting) */ + leftspaces = width - cvtwidth; + } + } + while (--leftspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + if (signwidth) { + rv = (*ss->stuff)(ss, &sign, 1); + if (rv < 0) { + return rv; + } + } + while (--precwidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + while (--zerowidth >= 0) { + rv = (*ss->stuff)(ss, "0", 1); + if (rv < 0) { + return rv; + } + } + rv = (*ss->stuff)(ss, src, srclen); + if (rv < 0) { + return rv; + } + while (--rightspaces >= 0) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + return 0; +} + +/* +** Convert a long into its printable form +*/ +static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (num == 0)) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (num) { + int digit = (((unsigned long)num) % radix) & 0xF; + *--cvt = hexp[digit]; + digits++; + num = (long)((unsigned long)num) / radix; + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a 64-bit integer into its printable form +*/ +static int cvt_ll(SprintfState *ss, PRInt64 num, int width, int prec, int radix, + int type, int flags, const char *hexp) +{ + char cvtbuf[100]; + char *cvt; + int digits; + PRInt64 rad; + + /* according to the man page this needs to happen */ + if ((prec == 0) && (LL_IS_ZERO(num))) { + return 0; + } + + /* + ** Converting decimal is a little tricky. In the unsigned case we + ** need to stop when we hit 10 digits. In the signed case, we can + ** stop when the number is zero. + */ + LL_I2L(rad, radix); + cvt = cvtbuf + sizeof(cvtbuf); + digits = 0; + while (!LL_IS_ZERO(num)) { + PRInt32 digit; + PRInt64 quot, rem; + LL_UDIVMOD(", &rem, num, rad); + LL_L2I(digit, rem); + *--cvt = hexp[digit & 0xf]; + digits++; + num = quot; + } + if (digits == 0) { + *--cvt = '0'; + digits++; + } + + /* + ** Now that we have the number converted without its sign, deal with + ** the sign and zero padding. + */ + return fill_n(ss, cvt, digits, width, prec, type, flags); +} + +/* +** Convert a double precision floating point number into its printable +** form. +** +** XXX stop using sprintf to convert floating point +*/ +static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) +{ + char fin[20]; + char fout[300]; + int amount = fmt1 - fmt0; + + PR_ASSERT((amount > 0) && (amount < sizeof(fin))); + if (amount >= sizeof(fin)) { + /* Totally bogus % command to sprintf. Just ignore it */ + return 0; + } + memcpy(fin, fmt0, amount); + fin[amount] = 0; + + /* Convert floating point using the native sprintf code */ +#ifdef DEBUG + { + const char *p = fin; + while (*p) { + PR_ASSERT(*p != 'L'); + p++; + } + } +#endif + sprintf(fout, fin, d); + + /* + ** This assert will catch overflow's of fout, when building with + ** debugging on. At least this way we can track down the evil piece + ** of calling code and fix it! + */ + PR_ASSERT(strlen(fout) < sizeof(fout)); + + return (*ss->stuff)(ss, fout, strlen(fout)); +} + +/* +** Convert a string into its printable form. "width" is the output +** width. "prec" is the maximum number of characters of "s" to output, +** where -1 means until NUL. +*/ +static int cvt_s(SprintfState *ss, const char *s, int width, int prec, + int flags) +{ + int slen; + + if (prec == 0) + return 0; + + /* Limit string length by precision value */ + slen = s ? strlen(s) : 6; + if (prec > 0) { + if (prec < slen) { + slen = prec; + } + } + + /* and away we go */ + return fill2(ss, s ? s : "(null)", slen, width, flags); +} + +/* +** BiuldArgArray stands for Numbered Argument list Sprintf +** for example, +** fmp = "%4$i, %2$d, %3s, %1d"; +** the number must start from 1, and no gap among them +*/ + +static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ) +{ + int number = 0, cn = 0, i; + const char* p; + char c; + struct NumArgState* nas; + + + /* + ** set the l10n_debug flag + ** this routine should be executed only once + ** 'cause getenv does take time + */ + if( !l10n_debug_init ){ + l10n_debug_init = PR_TRUE; + p = getenv( "NETSCAPE_LOCALIZATION_DEBUG" ); + if( ( p != NULL ) && ( *p == '1' ) ){ + l10n_debug = PR_TRUE; + } + } + + + /* + ** first pass: + ** detemine how many legal % I have got, then allocate space + */ + + p = fmt; + *rv = 0; + i = 0; + while( ( c = *p++ ) != 0 ){ + if( c != '%' ) + continue; + if( ( c = *p++ ) == '%' ) /* skip %% case */ + continue; + + while( c != 0 ){ + if( c > '9' || c < '0' ){ + if( c == '$' ){ /* numbered argument csae */ + if( i > 0 ){ + *rv = -1; + if( l10n_debug ) + printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt ); + return NULL; + } + number++; + break; + + } else{ /* non-numbered argument case */ + if( number > 0 ){ + if( l10n_debug ) + printf( "either no *OR* all arguments are numbered \"%s\"\n", fmt ); + *rv = -1; + return NULL; + } + i = 1; + break; + } + } + + c = *p++; + } + } + + if( number == 0 ){ + return NULL; + } + + + if( number > NAS_DEFAULT_NUM ){ + nas = (struct NumArgState*)PR_MALLOC( number * sizeof( struct NumArgState ) ); + if( !nas ){ + *rv = -1; + if( l10n_debug ) + printf( "PR_MALLOC() error for \"%s\"\n", fmt ); + return NULL; + } + } else { + nas = nasArray; + } + + for( i = 0; i < number; i++ ){ + nas[i].type = TYPE_UNKNOWN; + } + + + /* + ** second pass: + ** set nas[].type + */ + + p = fmt; + while( ( c = *p++ ) != 0 ){ + if( c != '%' ) continue; + c = *p++; + if( c == '%' ) continue; + + cn = 0; + while( c && c != '$' ){ /* should imporve error check later */ + cn = cn*10 + c - '0'; + c = *p++; + } + + if( !c || cn < 1 || cn > number ){ + *rv = -1; + if( l10n_debug ) + printf( "invalid argument number (valid range [1, %d]), \"%s\"\n", number, fmt ); + break; + } + + /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ + cn--; + if( nas[cn].type != TYPE_UNKNOWN ) + continue; + + c = *p++; + + /* width */ + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + *rv = -1; + if( l10n_debug ) + printf( "* width specifier not support for numbered arguments \"%s\"\n", fmt ); + break; + } else { + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + } + + /* precision */ + if (c == '.') { + c = *p++; + if (c == '*') { + /* not supported feature, for the argument is not numbered */ + if( l10n_debug ) + printf( "* precision specifier not support for numbered arguments \"%s\"\n", fmt ); + *rv = -1; + break; + } else { + while ((c >= '0') && (c <= '9')) { + c = *p++; + } + } + } + + /* size */ + nas[cn].type = TYPE_INTN; + if (c == 'h') { + nas[cn].type = TYPE_INT16; + c = *p++; + } else if (c == 'L') { + /* XXX not quite sure here */ + nas[cn].type = TYPE_INT64; + c = *p++; + } else if (c == 'l') { + nas[cn].type = TYPE_INT32; + c = *p++; + if (c == 'l') { + nas[cn].type = TYPE_INT64; + c = *p++; + } + } + + /* format */ + switch (c) { + case 'd': + case 'c': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + break; + + case 'e': + case 'f': + case 'g': + nas[ cn ].type = TYPE_DOUBLE; + break; + + case 'p': + /* XXX should use cpp */ + if (sizeof(void *) == sizeof(PRInt32)) { + nas[ cn ].type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(PRInt64)) { + nas[ cn ].type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(PRIntn)) { + nas[ cn ].type = TYPE_UINTN; + } else { + nas[ cn ].type = TYPE_UNKNOWN; + } + break; + + case 'C': + case 'S': + case 'E': + case 'G': + /* XXX not supported I suppose */ + PR_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + + case 's': + nas[ cn ].type = TYPE_STRING; + break; + + case 'n': + nas[ cn ].type = TYPE_INTSTR; + break; + + default: + PR_ASSERT(0); + nas[ cn ].type = TYPE_UNKNOWN; + break; + } + + /* get a legal para. */ + if( nas[ cn ].type == TYPE_UNKNOWN ){ + if( l10n_debug ) + printf( "unknown type \"%s\"\n", fmt ); + *rv = -1; + break; + } + } + + + /* + ** third pass + ** fill the nas[cn].ap + */ + + if( *rv < 0 ){ + if( nas != nasArray ) + PR_DELETE( nas ); + return NULL; + } + + cn = 0; + while( cn < number ){ + if( nas[cn].type == TYPE_UNKNOWN ){ + cn++; + continue; + } + + VARARGS_ASSIGN(nas[cn].ap, ap); + + switch( nas[cn].type ){ + case TYPE_INT16: + case TYPE_UINT16: + case TYPE_INTN: + case TYPE_UINTN: (void)va_arg( ap, PRIntn ); break; + + case TYPE_INT32: (void)va_arg( ap, PRInt32 ); break; + + case TYPE_UINT32: (void)va_arg( ap, PRUint32 ); break; + + case TYPE_INT64: (void)va_arg( ap, PRInt64 ); break; + + case TYPE_UINT64: (void)va_arg( ap, PRUint64 ); break; + + case TYPE_STRING: (void)va_arg( ap, char* ); break; + + case TYPE_INTSTR: (void)va_arg( ap, PRIntn* ); break; + + case TYPE_DOUBLE: (void)va_arg( ap, double ); break; + + default: + if( nas != nasArray ) + PR_DELETE( nas ); + *rv = -1; + return NULL; + } + + cn++; + } + + + return nas; +} + +/* +** The workhorse sprintf code. +*/ +static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) +{ + char c; + int flags, width, prec, radix, type; + union { + char ch; + int i; + long l; + PRInt64 ll; + double d; + const char *s; + int *ip; + } u; + const char *fmt0; + static char *hex = "0123456789abcdef"; + static char *HEX = "0123456789ABCDEF"; + char *hexp; + int rv, i; + struct NumArgState* nas = NULL; + struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; + char pattern[20]; + const char* dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ + + + /* + ** build an argument array, IF the fmt is numbered argument + ** list style, to contain the Numbered Argument list pointers + */ + + nas = BuildArgArray( fmt, ap, &rv, nasArray ); + if( rv < 0 ){ + /* the fmt contains error Numbered Argument format, jliu@netscape.com */ + PR_ASSERT(0); + return rv; + } + + while ((c = *fmt++) != 0) { + if (c != '%') { + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + fmt0 = fmt - 1; + + /* + ** Gobble up the % format string. Hopefully we have handled all + ** of the strange cases! + */ + flags = 0; + c = *fmt++; + if (c == '%') { + /* quoting a % with %% */ + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + continue; + } + + if( nas != NULL ){ + /* the fmt contains the Numbered Arguments feature */ + i = 0; + while( c && c != '$' ){ /* should imporve error check later */ + i = ( i * 10 ) + ( c - '0' ); + c = *fmt++; + } + + if( nas[i-1].type == TYPE_UNKNOWN ){ + if( l10n_debug ) + printf( "numbered argument type unknown\n" ); + if( nas && ( nas != nasArray ) ) + PR_DELETE( nas ); + return -1; + } + + ap = nas[i-1].ap; + dolPt = fmt; + c = *fmt++; + } + + /* + * Examine optional flags. Note that we do not implement the + * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is + * somewhat ambiguous and not ideal, which is perhaps why + * the various sprintf() implementations are inconsistent + * on this feature. + */ + while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { + if (c == '-') flags |= _LEFT; + if (c == '+') flags |= _SIGNED; + if (c == ' ') flags |= _SPACED; + if (c == '0') flags |= _ZEROS; + c = *fmt++; + } + if (flags & _SIGNED) flags &= ~_SPACED; + if (flags & _LEFT) flags &= ~_ZEROS; + + /* width */ + if (c == '*') { + c = *fmt++; + width = va_arg(ap, int); + } else { + width = 0; + while ((c >= '0') && (c <= '9')) { + width = (width * 10) + (c - '0'); + c = *fmt++; + } + } + + /* precision */ + prec = -1; + if (c == '.') { + c = *fmt++; + if (c == '*') { + c = *fmt++; + prec = va_arg(ap, int); + } else { + prec = 0; + while ((c >= '0') && (c <= '9')) { + prec = (prec * 10) + (c - '0'); + c = *fmt++; + } + } + } + + /* size */ + type = TYPE_INTN; + if (c == 'h') { + type = TYPE_INT16; + c = *fmt++; + } else if (c == 'L') { + /* XXX not quite sure here */ + type = TYPE_INT64; + c = *fmt++; + } else if (c == 'l') { + type = TYPE_INT32; + c = *fmt++; + if (c == 'l') { + type = TYPE_INT64; + c = *fmt++; + } + } + + /* format */ + hexp = hex; + switch (c) { + case 'd': case 'i': /* decimal/integer */ + radix = 10; + goto fetch_and_convert; + + case 'o': /* octal */ + radix = 8; + type |= 1; + goto fetch_and_convert; + + case 'u': /* unsigned decimal */ + radix = 10; + type |= 1; + goto fetch_and_convert; + + case 'x': /* unsigned hex */ + radix = 16; + type |= 1; + goto fetch_and_convert; + + case 'X': /* unsigned HEX */ + radix = 16; + hexp = HEX; + type |= 1; + goto fetch_and_convert; + + fetch_and_convert: + switch (type) { + case TYPE_INT16: + u.l = va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= _NEG; + } + goto do_long; + case TYPE_UINT16: + u.l = va_arg(ap, int) & 0xffff; + goto do_long; + case TYPE_INTN: + u.l = va_arg(ap, int); + if (u.l < 0) { + u.l = -u.l; + flags |= _NEG; + } + goto do_long; + case TYPE_UINTN: + u.l = (long)va_arg(ap, unsigned int); + goto do_long; + + case TYPE_INT32: + u.l = va_arg(ap, PRInt32); + if (u.l < 0) { + u.l = -u.l; + flags |= _NEG; + } + goto do_long; + case TYPE_UINT32: + u.l = (long)va_arg(ap, PRUint32); + do_long: + rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + + case TYPE_INT64: + u.ll = va_arg(ap, PRInt64); + if (!LL_GE_ZERO(u.ll)) { + LL_NEG(u.ll, u.ll); + flags |= _NEG; + } + goto do_longlong; + case TYPE_UINT64: + u.ll = va_arg(ap, PRUint64); + do_longlong: + rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); + if (rv < 0) { + return rv; + } + break; + } + break; + + case 'e': + case 'E': + case 'f': + case 'g': + u.d = va_arg(ap, double); + if( nas != NULL ){ + i = fmt - dolPt; + if( i < sizeof( pattern ) ){ + pattern[0] = '%'; + memcpy( &pattern[1], dolPt, i ); + rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); + } + } else + rv = cvt_f(ss, u.d, fmt0, fmt); + + if (rv < 0) { + return rv; + } + break; + + case 'c': + u.ch = va_arg(ap, int); + if ((flags & _LEFT) == 0) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + rv = (*ss->stuff)(ss, &u.ch, 1); + if (rv < 0) { + return rv; + } + if (flags & _LEFT) { + while (width-- > 1) { + rv = (*ss->stuff)(ss, " ", 1); + if (rv < 0) { + return rv; + } + } + } + break; + + case 'p': + if (sizeof(void *) == sizeof(PRInt32)) { + type = TYPE_UINT32; + } else if (sizeof(void *) == sizeof(PRInt64)) { + type = TYPE_UINT64; + } else if (sizeof(void *) == sizeof(int)) { + type = TYPE_UINTN; + } else { + PR_ASSERT(0); + break; + } + radix = 16; + goto fetch_and_convert; + +#if 0 + case 'C': + case 'S': + case 'E': + case 'G': + /* XXX not supported I suppose */ + PR_ASSERT(0); + break; +#endif + + case 's': + u.s = va_arg(ap, const char*); + rv = cvt_s(ss, u.s, width, prec, flags); + if (rv < 0) { + return rv; + } + break; + + case 'n': + u.ip = va_arg(ap, int*); + if (u.ip) { + *u.ip = ss->cur - ss->base; + } + break; + + default: + /* Not a % token after all... skip it */ +#if 0 + PR_ASSERT(0); +#endif + rv = (*ss->stuff)(ss, "%", 1); + if (rv < 0) { + return rv; + } + rv = (*ss->stuff)(ss, fmt - 1, 1); + if (rv < 0) { + return rv; + } + } + } + + /* Stuff trailing NUL */ + rv = (*ss->stuff)(ss, "\0", 1); + + if( nas && ( nas != nasArray ) ){ + PR_DELETE( nas ); + } + + return rv; +} + +/************************************************************************/ + +static int FuncStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + int rv; + + rv = (*ss->func)(ss->arg, sp, len); + if (rv < 0) { + return rv; + } + ss->maxlen += len; + return 0; +} + +PR_IMPLEMENT(PRUint32) PR_sxprintf(PRStuffFunc func, void *arg, + const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = PR_vsxprintf(func, arg, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vsxprintf(PRStuffFunc func, void *arg, + const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = FuncStuff; + ss.func = func; + ss.arg = arg; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + return (rv < 0) ? (PRUint32)-1 : ss.maxlen; +} + +/* +** Stuff routine that automatically grows the malloc'd output buffer +** before it overflows. +*/ +static int GrowStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + ptrdiff_t off; + char *newbase; + PRUint32 newlen; + + off = ss->cur - ss->base; + if (off + len >= ss->maxlen) { + /* Grow the buffer */ + newlen = ss->maxlen + ((len > 32) ? len : 32); + if (ss->base) { + newbase = (char*) PR_REALLOC(ss->base, newlen); + } else { + newbase = (char*) PR_MALLOC(newlen); + } + if (!newbase) { + /* Ran out of memory */ + return -1; + } + ss->base = newbase; + ss->maxlen = newlen; + ss->cur = ss->base + off; + } + + /* Copy data */ + while (len) { + --len; + *ss->cur++ = *sp++; + } + PR_ASSERT((PRUint32)(ss->cur - ss->base) <= ss->maxlen); + return 0; +} + +/* +** sprintf into a malloc'd buffer +*/ +PR_IMPLEMENT(char *) PR_smprintf(const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = PR_vsmprintf(fmt, ap); + va_end(ap); + return rv; +} + +/* +** Free memory allocated, for the caller, by PR_smprintf +*/ +PR_IMPLEMENT(void) PR_smprintf_free(char *mem) +{ + PR_DELETE(mem); +} + +PR_IMPLEMENT(char *) PR_vsmprintf(const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + PR_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + +/* +** Stuff routine that discards overflow data +*/ +static int LimitStuff(SprintfState *ss, const char *sp, PRUint32 len) +{ + PRUint32 limit = ss->maxlen - (ss->cur - ss->base); + + if (len > limit) { + len = limit; + } + while (len) { + --len; + *ss->cur++ = *sp++; + } + return 0; +} + +/* +** sprintf into a fixed size buffer. Make sure there is a NUL at the end +** when finished. +*/ +PR_IMPLEMENT(PRUint32) PR_snprintf(char *out, PRUint32 outlen, const char *fmt, ...) +{ + va_list ap; + int rv; + + PR_ASSERT((PRInt32)outlen > 0); + if ((PRInt32)outlen <= 0) { + return 0; + } + + va_start(ap, fmt); + rv = PR_vsnprintf(out, outlen, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vsnprintf(char *out, PRUint32 outlen,const char *fmt, + va_list ap) +{ + SprintfState ss; + PRUint32 n; + + PR_ASSERT((PRInt32)outlen > 0); + if ((PRInt32)outlen <= 0) { + return 0; + } + + ss.stuff = LimitStuff; + ss.base = out; + ss.cur = out; + ss.maxlen = outlen; + (void) dosprintf(&ss, fmt, ap); + + /* If we added chars, and we didn't append a null, do it now. */ + if( (ss.cur != ss.base) && (*(ss.cur - 1) != '\0') ) + *(--ss.cur) = '\0'; + + n = ss.cur - ss.base; + return n ? n - 1 : n; +} + +PR_IMPLEMENT(char *) PR_sprintf_append(char *last, const char *fmt, ...) +{ + va_list ap; + char *rv; + + va_start(ap, fmt); + rv = PR_vsprintf_append(last, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(char *) PR_vsprintf_append(char *last, const char *fmt, va_list ap) +{ + SprintfState ss; + int rv; + + ss.stuff = GrowStuff; + if (last) { + int lastlen = strlen(last); + ss.base = last; + ss.cur = last + lastlen; + ss.maxlen = lastlen; + } else { + ss.base = 0; + ss.cur = 0; + ss.maxlen = 0; + } + rv = dosprintf(&ss, fmt, ap); + if (rv < 0) { + if (ss.base) { + PR_DELETE(ss.base); + } + return 0; + } + return ss.base; +} + diff --git a/pr/src/io/prscanf.c b/pr/src/io/prscanf.c new file mode 100644 index 00000000..4a23543f --- /dev/null +++ b/pr/src/io/prscanf.c @@ -0,0 +1,637 @@ +/* -*- 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. + */ + +/* + * Scan functions for NSPR types + * + * Author: Wan-Teh Chang + * + * Acknowledgment: The implementation is inspired by the source code + * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992. + */ + +#include <limits.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "prprf.h" +#include "prdtoa.h" +#include "prlog.h" +#include "prerror.h" + +/* + * A function that reads a character from 'stream'. + * Returns the character read, or EOF if end of stream is reached. + */ +typedef int (*_PRGetCharFN)(void *stream); + +/* + * A function that pushes the character 'ch' back to 'stream'. + */ +typedef void (*_PRUngetCharFN)(void *stream, int ch); + +/* + * The size specifier for the integer and floating point number + * conversions in format control strings. + */ +typedef enum { + _PR_size_none, /* No size specifier is given */ + _PR_size_h, /* The 'h' specifier, suggesting "short" */ + _PR_size_l, /* The 'l' specifier, suggesting "long" */ + _PR_size_L, /* The 'L' specifier, meaning a 'long double' */ + _PR_size_ll /* The 'll' specifier, suggesting "long long" */ +} _PRSizeSpec; + +/* + * The collection of data that is passed between the scan function + * and its subordinate functions. The fields of this structure + * serve as the input or output arguments for these functions. + */ +typedef struct { + _PRGetCharFN get; /* get a character from input stream */ + _PRUngetCharFN unget; /* unget (push back) a character */ + void *stream; /* argument for get and unget */ + va_list ap; /* the variable argument list */ + int nChar; /* number of characters read from 'stream' */ + + PRBool assign; /* assign, or suppress assignment? */ + int width; /* field width */ + _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */ + + PRBool converted; /* is the value actually converted? */ +} ScanfState; + +#define GET(state) ((state)->nChar++, (state)->get((state)->stream)) +#define UNGET(state, ch) \ + ((state)->nChar--, (state)->unget((state)->stream, ch)) + +/* + * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH, + * are always used together. + * + * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return + * value to 'ch' only if we have not exceeded the field width of + * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of + * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true. + */ + +#define GET_IF_WITHIN_WIDTH(state, ch) \ + if (--(state)->width >= 0) { \ + (ch) = GET(state); \ + } +#define WITHIN_WIDTH(state) ((state)->width >= 0) + +/* + * _pr_strtoull: + * Convert a string to an unsigned 64-bit integer. The string + * 'str' is assumed to be a representation of the integer in + * base 'base'. + * + * Warning: + * - Only handle base 8, 10, and 16. + * - No overflow checking. + */ + +static PRUint64 +_pr_strtoull(const char *str, char **endptr, int base) +{ + static const int BASE_MAX = 16; + static const char digits[] = "0123456789abcdef"; + char *digitPtr; + PRUint64 x; /* return value */ + PRInt64 base64; + const char *cPtr; + PRBool negative; + const char *digitStart; + + PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16); + if (base < 0 || base == 1 || base > BASE_MAX) { + if (endptr) { + *endptr = (char *) str; + return LL_ZERO; + } + } + + cPtr = str; + while (isspace(*cPtr)) { + ++cPtr; + } + + negative = PR_FALSE; + if (*cPtr == '-') { + negative = PR_TRUE; + cPtr++; + } else if (*cPtr == '+') { + cPtr++; + } + + if (base == 16) { + if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) { + cPtr += 2; + } + } else if (base == 0) { + if (*cPtr != '0') { + base = 10; + } else if (cPtr[1] == 'x' || cPtr[1] == 'X') { + base = 16; + cPtr += 2; + } else { + base = 8; + } + } + PR_ASSERT(base != 0); + LL_I2L(base64, base); + digitStart = cPtr; + + /* Skip leading zeros */ + while (*cPtr == '0') { + cPtr++; + } + + LL_I2L(x, 0); + while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) { + PRUint64 d; + + LL_I2L(d, (digitPtr - digits)); + LL_MUL(x, x, base64); + LL_ADD(x, x, d); + cPtr++; + } + + if (cPtr == digitStart) { + if (endptr) { + *endptr = (char *) str; + } + return LL_ZERO; + } + + if (negative) { + LL_NEG(x, x); + } + + if (endptr) { + *endptr = (char *) cPtr; + } + return x; +} + +/* + * The maximum field width (in number of characters) that is enough + * (may be more than necessary) to represent a 64-bit integer or + * floating point number. + */ +#define FMAX 31 +#define DECIMAL_POINT '.' + +static PRStatus +GetInt(ScanfState *state, int code) +{ + char buf[FMAX + 1], *p; + int ch; + static const char digits[] = "0123456789abcdefABCDEF"; + PRBool seenDigit = PR_FALSE; + int base; + int dlen; + + switch (code) { + case 'd': case 'u': + base = 10; + break; + case 'i': + base = 0; + break; + case 'x': case 'X': case 'p': + base = 16; + break; + case 'o': + base = 8; + break; + default: + return PR_FAILURE; + } + if (state->width == 0 || state->width > FMAX) { + state->width = FMAX; + } + p = buf; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + if (WITHIN_WIDTH(state) && ch == '0') { + seenDigit = PR_TRUE; + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) + && (ch == 'x' || ch == 'X') + && (base == 0 || base == 16)) { + base = 16; + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } else if (base == 0) { + base = 8; + } + } + if (base == 0 || base == 10) { + dlen = 10; + } else if (base == 8) { + dlen = 8; + } else { + PR_ASSERT(base == 16); + dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */ + } + while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + if (WITHIN_WIDTH(state)) { + UNGET(state, ch); + } + if (!seenDigit) { + return PR_FAILURE; + } + *p = '\0'; + if (state->assign) { + if (code == 'd' || code == 'i') { + if (state->sizeSpec == _PR_size_ll) { + PRInt64 llval = _pr_strtoull(buf, NULL, base); + *va_arg(state->ap, PRInt64 *) = llval; + } else { + long lval = strtol(buf, NULL, base); + + if (state->sizeSpec == _PR_size_none) { + *va_arg(state->ap, PRIntn *) = lval; + } else if (state->sizeSpec == _PR_size_h) { + *va_arg(state->ap, PRInt16 *) = (PRInt16)lval; + } else if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRInt32 *) = lval; + } else { + return PR_FAILURE; + } + } + } else { + if (state->sizeSpec == _PR_size_ll) { + PRUint64 llval = _pr_strtoull(buf, NULL, base); + *va_arg(state->ap, PRUint64 *) = llval; + } else { + unsigned long lval = strtoul(buf, NULL, base); + + if (state->sizeSpec == _PR_size_none) { + *va_arg(state->ap, PRUintn *) = lval; + } else if (state->sizeSpec == _PR_size_h) { + *va_arg(state->ap, PRUint16 *) = (PRUint16)lval; + } else if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRUint32 *) = lval; + } else { + return PR_FAILURE; + } + } + } + state->converted = PR_TRUE; + } + return PR_SUCCESS; +} + +static PRStatus +GetFloat(ScanfState *state) +{ + char buf[FMAX + 1], *p; + int ch; + PRBool seenDigit = PR_FALSE; + + if (state->width == 0 || state->width > FMAX) { + state->width = FMAX; + } + p = buf; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + seenDigit = PR_TRUE; + } + } + + /* + * This is not robust. For example, "1.2e+" would confuse + * the code below to read 'e' and '+', only to realize that + * it should have stopped at "1.2". But we can't push back + * more than one character, so there is nothing I can do. + */ + + /* Parse exponent */ + if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + while (WITHIN_WIDTH(state) && isdigit(ch)) { + *p++ = ch; + GET_IF_WITHIN_WIDTH(state, ch); + } + } + if (WITHIN_WIDTH(state)) { + UNGET(state, ch); + } + if (!seenDigit) { + return PR_FAILURE; + } + *p = '\0'; + if (state->assign) { + PRFloat64 dval = PR_strtod(buf, NULL); + + state->converted = PR_TRUE; + if (state->sizeSpec == _PR_size_l) { + *va_arg(state->ap, PRFloat64 *) = dval; + } else if (state->sizeSpec == _PR_size_L) { +#if defined(OSF1) || defined(IRIX) + *va_arg(state->ap, double *) = dval; +#else + *va_arg(state->ap, long double *) = dval; +#endif + } else { + *va_arg(state->ap, float *) = dval; + } + } + return PR_SUCCESS; +} + +/* + * Convert, and return the end of the conversion spec. + */ + +static const char * +Convert(ScanfState *state, const char *fmt) +{ + const char *cPtr; + int ch; + char *cArg; + + state->converted = PR_FALSE; + cPtr = fmt; + if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') { + do { + ch = GET(state); + } while (isspace(ch)); + UNGET(state, ch); + } + switch (*cPtr) { + case 'c': + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + if (state->width == 0) { + state->width = 1; + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if (ch == EOF) { + return NULL; + } else if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + state->converted = PR_TRUE; + } + break; + case 'p': + case 'd': case 'i': case 'o': + case 'u': case 'x': case 'X': + if (GetInt(state, *cPtr) == PR_FAILURE) { + return NULL; + } + break; + case 'e': case 'E': case 'f': + case 'g': case 'G': + if (GetFloat(state) == PR_FAILURE) { + return NULL; + } + break; + case 'n': + /* do not consume any input */ + if (state->assign) { + switch (state->sizeSpec) { + case _PR_size_none: + *va_arg(state->ap, PRIntn *) = state->nChar; + break; + case _PR_size_h: + *va_arg(state->ap, PRInt16 *) = state->nChar; + break; + case _PR_size_l: + *va_arg(state->ap, PRInt32 *) = state->nChar; + break; + case _PR_size_ll: + LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar); + break; + default: + PR_ASSERT(0); + } + } + break; + case 's': + if (state->width == 0) { + state->width = INT_MAX; + } + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if ((ch == EOF) || isspace(ch)) { + UNGET(state, ch); + break; + } + if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + *cArg = '\0'; + state->converted = PR_TRUE; + } + break; + case '%': + ch = GET(state); + if (ch != '%') { + UNGET(state, ch); + return NULL; + } + break; + case '[': + { + PRBool complement = PR_FALSE; + const char *closeBracket; + size_t n; + + if (*++cPtr == '^') { + complement = PR_TRUE; + cPtr++; + } + closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']'); + if (closeBracket == NULL) { + return NULL; + } + n = closeBracket - cPtr; + if (state->width == 0) { + state->width = INT_MAX; + } + if (state->assign) { + cArg = va_arg(state->ap, char *); + } + for (; state->width > 0; state->width--) { + ch = GET(state); + if ((ch == EOF) + || (!complement && !memchr(cPtr, ch, n)) + || (complement && memchr(cPtr, ch, n))) { + UNGET(state, ch); + break; + } + if (state->assign) { + *cArg++ = ch; + } + } + if (state->assign) { + *cArg = '\0'; + state->converted = PR_TRUE; + } + cPtr = closeBracket; + } + break; + default: + return NULL; + } + return cPtr; +} + +static PRInt32 +DoScanf(ScanfState *state, const char *fmt) +{ + PRInt32 nConverted = 0; + const char *cPtr; + int ch; + + state->nChar = 0; + cPtr = fmt; + while (1) { + if (isspace(*cPtr)) { + do { + cPtr++; + } while (isspace(*cPtr)); + do { + ch = GET(state); + } while (isspace(ch)); + UNGET(state, ch); + } else if (*cPtr != '%') { + if (*cPtr == '\0') { + return nConverted; + } + ch = GET(state); + if (ch != *cPtr) { + UNGET(state, ch); + return nConverted; + } + cPtr++; + } else { + cPtr++; + state->assign = PR_TRUE; + if (*cPtr == '*') { + state->assign = PR_FALSE; + } + for (state->width = 0; isdigit(*cPtr); cPtr++) { + state->width = state->width * 10 + *cPtr - '0'; + } + state->sizeSpec = _PR_size_none; + if (*cPtr == 'h') { + cPtr++; + state->sizeSpec = _PR_size_h; + } else if (*cPtr == 'l') { + cPtr++; + if (*cPtr == 'l') { + cPtr++; + state->sizeSpec = _PR_size_ll; + } else { + state->sizeSpec = _PR_size_l; + } + } else if (*cPtr == 'L') { + cPtr++; + state->sizeSpec = _PR_size_L; + } + cPtr = Convert(state, cPtr); + if (cPtr == NULL) { + return (nConverted > 0 ? nConverted : EOF); + } + if (state->converted) { + nConverted++; + } + cPtr++; + } + } +} + +static int +StringGetChar(void *stream) +{ + char *cPtr = *((char **) stream); + + if (*cPtr == '\0') { + return EOF; + } else { + *((char **) stream) = cPtr + 1; + return *cPtr; + } +} + +static void +StringUngetChar(void *stream, int ch) +{ + char *cPtr = *((char **) stream); + + if (ch != EOF) { + *((char **) stream) = cPtr - 1; + } +} + +PR_IMPLEMENT(PRInt32) +PR_sscanf(const char *buf, const char *fmt, ...) +{ + PRInt32 rv; + ScanfState state; + + state.get = &StringGetChar; + state.unget = &StringUngetChar; + state.stream = (void *) &buf; + va_start(state.ap, fmt); + rv = DoScanf(&state, fmt); + va_end(state.ap); + return rv; +} diff --git a/pr/src/io/prsocket.c b/pr/src/io/prsocket.c new file mode 100644 index 00000000..53746ecd --- /dev/null +++ b/pr/src/io/prsocket.c @@ -0,0 +1,1519 @@ +/* -*- 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" + +#ifdef XP_UNIX +#include <fcntl.h> +#endif +#include <string.h> + +#if defined(SVR4) || defined(SUNOS4) +/* To pick up FIONREAD */ +#include <sys/filio.h> +#endif + +/************************************************************************/ + +static PRInt32 PR_CALLBACK SocketWritev(PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_size, +PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + int w = 0; + PRIOVec *tmp_iov = NULL; + int tmp_out; + int index, iov_cnt; + int count=0, sz = 0; /* 'count' is the return value. */ +#if defined(XP_UNIX) + struct timeval tv, *tvp; + fd_set wd; + + FD_ZERO(&wd); + if (timeout == PR_INTERVAL_NO_TIMEOUT) + tvp = NULL; + else if (timeout != PR_INTERVAL_NO_WAIT) { + tv.tv_sec = PR_IntervalToSeconds(timeout); + tv.tv_usec = PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + } +#endif + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + tmp_iov = (PRIOVec *)PR_CALLOC(iov_size * sizeof(PRIOVec)); + if (!tmp_iov) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + for (index=0; index<iov_size; index++) { + sz += iov[index].iov_len; + tmp_iov[index].iov_base = iov[index].iov_base; + tmp_iov[index].iov_len = iov[index].iov_len; + } + iov_cnt = iov_size; + + while (sz > 0) { + + w = _PR_MD_WRITEV(fd, tmp_iov, iov_cnt, timeout); + if (w < 0) { + count = -1; + break; + } + count += w; + if (fd->secret->nonblocking) { + break; + } + sz -= w; + + if (sz > 0) { + /* find the next unwritten vector */ + for ( index = 0, tmp_out = count; + tmp_out >= iov[index].iov_len; + tmp_out -= iov[index].iov_len, index++){;} /* nothing to execute */ + + + /* fill in the first partial read */ + tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[tmp_out]); + tmp_iov[0].iov_len = iov[index].iov_len - tmp_out; + index++; + + /* copy the remaining vectors */ + for (iov_cnt=1; index<iov_size; iov_cnt++, index++) { + tmp_iov[iov_cnt].iov_base = iov[index].iov_base; + tmp_iov[iov_cnt].iov_len = iov[index].iov_len; + } + } + } + + if (tmp_iov) + PR_DELETE(tmp_iov); + return count; +} + +/************************************************************************/ + +PR_IMPLEMENT(PRFileDesc *) PR_ImportTCPSocket(PRInt32 osfd) +{ +PRFileDesc *fd; + + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (fd != NULL) + _PR_MD_MAKE_NONBLOCK(fd); + else + _PR_MD_CLOSE_SOCKET(osfd); + return(fd); +} + +PR_IMPLEMENT(PRFileDesc *) PR_ImportUDPSocket(PRInt32 osfd) +{ +PRFileDesc *fd; + + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + if (fd != NULL) + _PR_MD_MAKE_NONBLOCK(fd); + else + _PR_MD_CLOSE_SOCKET(osfd); + return(fd); +} + +static PRStatus PR_CALLBACK SocketConnect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; /* Return value of _PR_MD_CONNECT */ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return PR_FAILURE; + } + + rv = _PR_MD_CONNECT(fd, addr, PR_NETADDR_SIZE(addr), timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("connect -> %d", rv)); + if (rv == 0) + return PR_SUCCESS; + else + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + PRInt32 osfd; + PRFileDesc *bottom = pd->fd; + int err, len; + + if (pd->out_flags & PR_POLL_NVAL) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((pd->out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) { + PR_ASSERT(pd->out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + osfd = bottom->secret->md.osfd; + +#if defined(XP_UNIX) + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + +#elif defined(WIN32) || defined(WIN16) + + if (pd->out_flags & PR_POLL_EXCEPT) { +#if defined(WIN32) +/* Note: There is a bug in Win32 WinSock. The sleep circumvents the +** bug. See wtc. /s lth. +*/ + Sleep(0); +#endif /* WIN32 */ + len = sizeof(err); + if (getsockopt(osfd, (int)SOL_SOCKET, SO_ERROR, (char *) &err, &len) + == SOCKET_ERROR) { + _PR_MD_MAP_GETSOCKOPT_ERROR(WSAGetLastError()); + return PR_FAILURE; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return PR_FAILURE; + } + + PR_ASSERT(pd->out_flags & PR_POLL_WRITE); + return PR_SUCCESS; + +#elif defined(XP_OS2) + + if (pd->out_flags & PR_POLL_EXCEPT) { + len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &len) + < 0) { + _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno()); + return PR_FAILURE; + } + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + } else { + PR_SetError(PR_UNKNOWN_ERROR, 0); + } + return PR_FAILURE; + } + + PR_ASSERT(pd->out_flags & PR_POLL_WRITE); + return PR_SUCCESS; + +#elif defined(XP_MAC) + + err = _MD_mac_get_nonblocking_connect_error(osfd); + if (err == -1) + return PR_FAILURE; + else + return PR_SUCCESS; + +#else + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +static PRFileDesc* PR_CALLBACK SocketAccept(PRFileDesc *fd, PRNetAddr *addr, +PRIntervalTime timeout) +{ + PRInt32 osfd; + PRFileDesc *fd2; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); +#ifdef WINNT + PRNetAddr addrCopy; +#endif + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + +#ifdef WINNT + if (addr == NULL) { + addr = &addrCopy; + } +#endif + al = sizeof(PRNetAddr); + osfd = _PR_MD_ACCEPT(fd, addr, &al, timeout); + if (osfd == -1) + return 0; + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + return NULL; + } + + fd2->secret->nonblocking = fd->secret->nonblocking; +#ifdef WINNT + fd2->secret->md.io_model_committed = PR_TRUE; + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); +#endif + + /* + * On some platforms, the new socket created by accept() + * inherits the nonblocking (or overlapped io) attribute + * of the listening socket. As an optimization, these + * platforms can skip the following _PR_MD_MAKE_NONBLOCK + * call. + */ +#if !defined(SOLARIS) && !defined(IRIX) && !defined(WINNT) + _PR_MD_MAKE_NONBLOCK(fd2); +#endif + + PR_ASSERT((NULL == addr) || (PR_NETADDR_SIZE(addr) == al)); +#if defined(_PR_INET6) + PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET) + || (addr->raw.family == AF_INET6)); +#else + PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET)); +#endif + + return fd2; +} + +#ifdef WINNT +PR_IMPLEMENT(PRFileDesc*) PR_NTFast_Accept(PRFileDesc *fd, PRNetAddr *addr, +PRIntervalTime timeout) +{ + PRInt32 osfd; + PRFileDesc *fd2; + PRIntn al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr addrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return 0; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return 0; + } + + if (addr == NULL) { + addr = &addrCopy; + } + al = PR_NETADDR_SIZE(addr); + osfd = _PR_MD_FAST_ACCEPT(fd, addr, &al, timeout, PR_TRUE, NULL, NULL); + if (osfd == -1) { + return 0; + } + + fd2 = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + if (!fd2) { + _PR_MD_CLOSE_SOCKET(osfd); + } else { + fd2->secret->nonblocking = fd->secret->nonblocking; + fd2->secret->md.io_model_committed = PR_TRUE; + PR_ASSERT(al == PR_NETADDR_SIZE(addr)); + fd2->secret->md.accepted_socket = PR_TRUE; + memcpy(&fd2->secret->md.peer_addr, addr, al); + } + return fd2; +} +#endif /* WINNT */ + + +static PRStatus PR_CALLBACK SocketBind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRInt32 result; + int one = 1; + +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + +#ifdef HAVE_SOCKET_REUSEADDR + if ( setsockopt (fd->secret->md.osfd, (int)SOL_SOCKET, SO_REUSEADDR, + (const void *)&one, sizeof(one) ) < 0) { + return PR_FAILURE; + } +#endif + + result = _PR_MD_BIND(fd, addr, PR_NETADDR_SIZE(addr)); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketListen(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 result; + + result = _PR_MD_LISTEN(fd, backlog); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketShutdown(PRFileDesc *fd, PRIntn how) +{ + PRInt32 result; + + result = _PR_MD_SHUTDOWN(fd, how); + if (result < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketRecv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv: fd=%p osfd=%d buf=%p amount=%d", + fd, fd->secret->md.osfd, buf, amount)); + rv = _PR_MD_RECV(fd, buf, amount, flags, timeout); + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("recv -> %d, error = %d, os error = %d", + rv, PR_GetError(), PR_GetOSError())); + return rv; +} + +static PRInt32 PR_CALLBACK SocketRead(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + return SocketRecv(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRInt32 PR_CALLBACK SocketSend(PRFileDesc *fd, const void *buf, PRInt32 amount, +PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + count = 0; + while (amount > 0) { + PR_LOG(_pr_io_lm, PR_LOG_MAX, + ("send: fd=%p osfd=%d buf=%p amount=%d", + fd, fd->secret->md.osfd, buf, amount)); + temp = _PR_MD_SEND(fd, buf, amount, flags, timeout); + if (temp < 0) { + count = -1; + break; + } + + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + + amount -= temp; + } + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("send -> %d", count)); + return count; +} + +static PRInt32 PR_CALLBACK SocketWrite(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + return SocketSend(fd, buf, amount, 0, PR_INTERVAL_NO_TIMEOUT); +} + +static PRStatus PR_CALLBACK SocketClose(PRFileDesc *fd) +{ + PRInt32 rv; + + if (!fd || fd->secret->state != _PR_FILEDESC_OPEN) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + + fd->secret->state = _PR_FILEDESC_CLOSED; + + rv = _PR_MD_CLOSE_SOCKET(fd->secret->md.osfd); + PR_FreeFileDesc(fd); + if (rv < 0) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketAvailable(PRFileDesc *fd) +{ + PRInt32 rv; + rv = _PR_MD_SOCKETAVAILABLE(fd); + return rv; +} + +static PRInt64 PR_CALLBACK SocketAvailable64(PRFileDesc *fd) +{ + PRInt64 rv; + LL_I2L(rv, _PR_MD_SOCKETAVAILABLE(fd)); + return rv; +} + +static PRStatus PR_CALLBACK SocketSync(PRFileDesc *fd) +{ +#if defined(XP_MAC) +#pragma unused (fd) +#endif + + return PR_SUCCESS; +} + +static PRInt32 PR_CALLBACK SocketSendTo( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 temp, count; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + + count = 0; + while (amount > 0) { + temp = _PR_MD_SENDTO(fd, buf, amount, flags, + addr, PR_NETADDR_SIZE(addr), timeout); + if (temp < 0) { + count = -1; + break; + } + count += temp; + if (fd->secret->nonblocking) { + break; + } + buf = (const void*) ((const char*)buf + temp); + amount -= temp; + } + return count; +} + +static PRInt32 PR_CALLBACK SocketRecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, +PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRInt32 rv; + PRUint32 al; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + + al = sizeof(PRNetAddr); + rv = _PR_MD_RECVFROM(fd, buf, amount, flags, addr, &al, timeout); + return rv; +} + +static PRInt32 PR_CALLBACK SocketAcceptRead(PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + +#if defined(WINNT) + { + PRInt32 newSock; + PRNetAddr *raddrCopy; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_ACCEPT_READ(sd, &newSock, raddr, buf, amount, timeout); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); + } + } + } +#else + rv = _PR_EmulateAcceptRead(sd, nd, raddr, buf, amount, timeout); +#endif + return rv; +} + +#ifdef WINNT +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead(PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRInt32 newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, NULL, NULL); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); + } + } + return rv; +} + +PR_IMPLEMENT(PRInt32) PR_NTFast_AcceptRead_WithTimeoutCallback( +PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, +PRIntervalTime timeout, +_PR_AcceptTimeoutCallback callback, +void *callbackArg) +{ + PRInt32 rv; + PRInt32 newSock; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRNetAddr *raddrCopy; + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } + *nd = NULL; + + if (raddr == NULL) { + raddr = &raddrCopy; + } + rv = _PR_MD_FAST_ACCEPT_READ(sd, &newSock, raddr, buf, amount, + timeout, PR_TRUE, callback, callbackArg); + if (rv < 0) { + rv = -1; + } else { + /* Successfully accepted and read; create the new PRFileDesc */ + *nd = PR_AllocFileDesc(newSock, PR_GetTCPMethods()); + if (*nd == 0) { + _PR_MD_CLOSE_SOCKET(newSock); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + rv = -1; + } else { + (*nd)->secret->md.io_model_committed = PR_TRUE; + (*nd)->secret->md.accepted_socket = PR_TRUE; + memcpy(&(*nd)->secret->md.peer_addr, *raddr, + PR_NETADDR_SIZE(*raddr)); + } + } + return rv; +} +#endif /* WINNT */ + +#ifdef WINNT +PR_IMPLEMENT(void) +PR_NTFast_UpdateAcceptContext(PRFileDesc *socket, PRFileDesc *acceptSocket) +{ + _PR_MD_UPDATE_ACCEPT_CONTEXT( + socket->secret->md.osfd, acceptSocket->secret->md.osfd); +} +#endif /* WINNT */ + +static PRInt32 PR_CALLBACK SocketTransmitFile(PRFileDesc *sd, PRFileDesc *fd, +const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, +PRIntervalTime timeout) +{ + PRInt32 rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + if (_PR_IO_PENDING(me)) { + PR_SetError(PR_IO_PENDING_ERROR, 0); + return -1; + } +#if defined(WINNT) + rv = _PR_MD_TRANSMITFILE( + sd, fd, + headers, hlen, flags, timeout); + if (rv < 0) { + rv = -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + /* + * This should be kept the same as SocketClose, except + * that _PR_MD_CLOSE_SOCKET(sd->secret->md.osfd) should + * not be called because the socket will be recycled. + */ + sd->secret->state = _PR_FILEDESC_CLOSED; + PR_FreeFileDesc(sd); + } +#else +#if defined(XP_UNIX) + /* + * On HPUX11, we could call _PR_HPUXTransmitFile(), but that + * would require that we not override the malloc() functions. + */ + rv = _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout); +#else /* XP_UNIX */ + rv = _PR_EmulateTransmitFile(sd, fd, headers, hlen, flags, + timeout); +#endif /* XP_UNIX */ +#endif /* WINNT */ + + return rv; +} + +static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETSOCKNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } + PR_ASSERT(addrlen == PR_NETADDR_SIZE(addr)); +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketGetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRInt32 result; + PRUint32 addrlen; + + addrlen = sizeof(PRNetAddr); + result = _PR_MD_GETPEERNAME(fd, addr, &addrlen); + if (result < 0) { + return PR_FAILURE; + } + PR_ASSERT(addrlen == PR_NETADDR_SIZE(addr)); +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + return PR_SUCCESS; +} + +static PRStatus PR_CALLBACK SocketGetSockOpt( + PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen) +{ + PRInt32 level, name; + PRStatus rv; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == optname) + { + PR_ASSERT(sizeof(PRIntn) <= *optlen); + *((PRIntn *) optval) = (PRIntn) fd->secret->nonblocking; + *optlen = sizeof(PRIntn); + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(optname, &level, &name); + if (PR_SUCCESS == rv) + { + if (PR_SockOpt_Linger == optname) + { + struct linger linger; + PRInt32 len = sizeof(linger); + rv = _PR_MD_GETSOCKOPT( + fd, level, name, (char *) &linger, &len); + if (PR_SUCCESS == rv) + { + ((PRLinger*)(optval))->polarity = linger.l_onoff + ? PR_TRUE : PR_FALSE; + ((PRLinger*)(optval))->linger = PR_SecondsToInterval( + linger.l_linger); + *optlen = sizeof(PRLinger); + } + } + else + { + rv = _PR_MD_GETSOCKOPT( + fd, level, name, optval, optlen); + } + } + return rv; +} + +static PRStatus PR_CALLBACK SocketSetSockOpt( + PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen) +{ + PRInt32 level, name; + PRStatus rv; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == optname) + { + PRBool fNonblocking = *((PRIntn *) optval) ? PR_TRUE : PR_FALSE; + PR_ASSERT(sizeof(PRIntn) == optlen); +#ifdef WINNT + PR_ASSERT((fd->secret->md.io_model_committed == PR_FALSE) + || (fd->secret->nonblocking == fNonblocking)); + if (fd->secret->md.io_model_committed + && (fd->secret->nonblocking != fNonblocking)) + { + /* + * On NT, once we have associated a socket with the io + * completion port, we can't disassociate it. So we + * can't change the nonblocking option of the socket + * afterwards. + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } +#endif + fd->secret->nonblocking = fNonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(optname, &level, &name); + if (PR_SUCCESS == rv) + { + if (PR_SockOpt_Linger == optname) + { + struct linger linger; + linger.l_onoff = ((PRLinger*)(optval))->polarity ? 1 : 0; + linger.l_linger = PR_IntervalToSeconds( + ((PRLinger*)(optval))->linger); + rv = _PR_MD_SETSOCKOPT( + fd, level, name, (char *) &linger, sizeof(linger)); + } + else + { + rv = _PR_MD_SETSOCKOPT( + fd, level, name, optval, optlen); + } + } + return rv; +} + +static PRIOMethods tcpMethods = { + PR_DESC_SOCKET_TCP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + SocketAccept, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + (PRPollFN)0, + SocketAcceptRead, + SocketTransmitFile, + SocketGetName, + SocketGetPeerName, + SocketGetSockOpt, + SocketSetSockOpt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption +}; + +static PRIOMethods udpMethods = { + PR_DESC_SOCKET_UDP, + SocketClose, + SocketRead, + SocketWrite, + SocketAvailable, + SocketAvailable64, + SocketSync, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + SocketWritev, + SocketConnect, + (PRAcceptFN)_PR_InvalidDesc, + SocketBind, + SocketListen, + SocketShutdown, + SocketRecv, + SocketSend, + SocketRecvFrom, + SocketSendTo, + (PRPollFN)0, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + SocketGetName, + SocketGetPeerName, + SocketGetSockOpt, + SocketSetSockOpt, + _PR_SocketGetSocketOption, + _PR_SocketSetSocketOption +}; + +PR_IMPLEMENT(PRIOMethods*) PR_GetTCPMethods() +{ + return &tcpMethods; +} + +PR_IMPLEMENT(PRIOMethods*) PR_GetUDPMethods() +{ + return &udpMethods; +} + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRInt32 osfd; + int one = 1; + PRFileDesc *fd; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + if (AF_INET != domain +#if defined(_PR_INET6) + && AF_INET6 != domain +#endif + ) { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return NULL; + } + osfd = _PR_MD_SOCKET(domain, type, proto); + if (osfd == -1) { + return 0; + } +#ifdef HAVE_SOCKET_KEEPALIVE + /* "Keep-alive" packets are specific to TCP. */ + if (domain == AF_INET && type == SOCK_STREAM) { + if (setsockopt(osfd, (int)SOL_SOCKET, SO_KEEPALIVE, + (const void *) &one, sizeof(one) ) < 0) { + _PR_MD_CLOSE_SOCKET(osfd); + return 0; + } + } +#endif + if (type == SOCK_STREAM) + fd = PR_AllocFileDesc(osfd, PR_GetTCPMethods()); + else + fd = PR_AllocFileDesc(osfd, PR_GetUDPMethods()); + /* + * Make the sockets non-blocking + */ + if (fd != NULL) + _PR_MD_MAKE_NONBLOCK(fd); + else + _PR_MD_CLOSE_SOCKET(osfd); + return fd; +} + +PR_IMPLEMENT(PRFileDesc *) PR_NewTCPSocket(void) +{ + PRInt32 domain = AF_INET; + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) { + domain = AF_INET6; + } +#endif + return PR_Socket(domain, SOCK_STREAM, 0); +} + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) +{ + PRInt32 domain = AF_INET; + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) { + domain = AF_INET6; + } +#endif + return PR_Socket(domain, SOCK_DGRAM, 0); +} + +PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[]) +{ +#ifdef XP_UNIX + PRInt32 rv, osfd[2]; + + rv = _PR_MD_SOCKETPAIR(AF_UNIX, SOCK_STREAM, 0, osfd); + if (rv == -1) { + return PR_FAILURE; + } + + f[0] = PR_AllocFileDesc(osfd[0], PR_GetTCPMethods()); + if (!f[0]) { + _PR_MD_CLOSE_SOCKET(osfd[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + f[1] = PR_AllocFileDesc(osfd[1], PR_GetTCPMethods()); + if (!f[1]) { + PR_Close(f[0]); + _PR_MD_CLOSE_SOCKET(osfd[1]); + /* PR_AllocFileDesc() has invoked PR_SetError(). */ + return PR_FAILURE; + } + _PR_MD_MAKE_NONBLOCK(f[0]); + _PR_MD_MAKE_NONBLOCK(f[1]); + return PR_SUCCESS; +#endif + + /* XXX: this needs to be implemented for MAC and NT */ +#ifdef XP_MAC +#pragma unused (f) + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); + return PR_FAILURE; +#endif + +#ifdef XP_PC + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +#endif +} + +PR_IMPLEMENT(PRInt32) +PR_FileDesc2NativeHandle(PRFileDesc *fd) +{ + if (fd) { + /* + * The fd may be layered. Chase the links to the + * bottom layer to get the osfd. + */ + PRFileDesc *bottom = fd; + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + return bottom->secret->md.osfd; + } else { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +} + +PR_IMPLEMENT(void) +PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PRInt32 handle) +{ + if (fd) + fd->secret->md.osfd = handle; +} + +/* + * _PR_EmulateTransmitFile + * + * Send file fd across socket sd. If headers is non-NULL, 'hlen' + * bytes of headers is sent before sending the file. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + */ + +PRInt32 _PR_EmulateTransmitFile(PRFileDesc *sd, PRFileDesc *fd, +const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, +PRIntervalTime timeout) +{ + PRInt32 rv, count = 0; + PRInt32 rlen; + PRThread *me = _PR_MD_CURRENT_THREAD(); + char *buf = NULL; +#define _TRANSMITFILE_BUFSIZE (16 * 1024) + + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + + buf = PR_MALLOC(_TRANSMITFILE_BUFSIZE); + if (buf == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + + /* + * send headers, first + */ + while (hlen) { + rv = PR_Send(sd, headers, hlen, 0, timeout); + if (rv < 0) { + /* PR_Send() has invoked PR_SetError(). */ + rv = -1; + goto done; + } else { + count += rv; + headers = (const void*) ((const char*)headers + rv); + hlen -= rv; + } + } + /* + * send file, next + */ + while ((rlen = PR_Read(fd, buf, _TRANSMITFILE_BUFSIZE)) > 0) { + while (rlen) { + char *bufptr = buf; + + rv = PR_Send(sd, bufptr, rlen,0,PR_INTERVAL_NO_TIMEOUT); + if (rv < 0) { + /* PR_Send() has invoked PR_SetError(). */ + rv = -1; + goto done; + } else { + count += rv; + bufptr = ((char*)bufptr + rv); + rlen -= rv; + } + } + } + if (rlen == 0) { + /* + * end-of-file + */ + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) + PR_Close(sd); + rv = count; + } else { + PR_ASSERT(rlen < 0); + /* PR_Read() has invoked PR_SetError(). */ + rv = -1; + } + +done: + if (buf) + PR_DELETE(buf); + return rv; +} + +/* + * _PR_EmulateAcceptRead + * + * Accept an incoming connection on sd, set *nd to point to the + * newly accepted socket, read 'amount' bytes from the accepted + * socket. + * + * buf is a buffer of length = (amount + sizeof(PRNetAddr)) + * *raddr points to the PRNetAddr of the accepted connection upon + * return + * + * return number of bytes read or -1 on error + * + */ +PRInt32 _PR_EmulateAcceptRead(PRFileDesc *sd, PRFileDesc **nd, +PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + PRInt32 rv; + PRFileDesc *newsockfd; + PRNetAddr remote; + PRIntervalTime start, elapsed; + + if (PR_INTERVAL_NO_TIMEOUT != timeout) { + start = PR_IntervalNow(); + } + if ((newsockfd = PR_Accept(sd, &remote, timeout)) == NULL) { + return -1; + } + + if (PR_INTERVAL_NO_TIMEOUT != timeout) { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + goto failed; + } else { + timeout = timeout - elapsed; + } + } + + rv = PR_Recv(newsockfd, buf, amount, 0, timeout); + if (rv >= 0) { + *nd = newsockfd; + *raddr = (PRNetAddr *)((char *) buf + amount); + memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote)); + return rv; + } + +failed: + PR_Close(newsockfd); + return -1; +} + +/* +** Select compatibility +** +*/ + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set) +{ + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index, index2; + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index; + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + + +#if !defined(NEED_SELECT) +#if !defined(XP_MAC) +#include "obsolete/probslet.h" +#else +#include "probslet.h" +#endif + +#define PD_INCR 20 + +static PRPollDesc *_pr_setfd( + PR_fd_set *set, PRInt16 flags, PRPollDesc *polldesc) +{ + PRUintn fsidx, pdidx; + PRPollDesc *poll = polldesc; + + if (NULL == set) return poll; + + /* First set the pr file handle osfds */ + for (fsidx = 0; fsidx < set->hsize; fsidx++) + { + for (pdidx = 0; 1; pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = PR_Realloc( + poll, (pdidx + 1 + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[pdidx + PD_INCR].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->harray[fsidx])) + { + /* PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); */ + /* either empty or prevously defined */ + poll[pdidx].fd = set->harray[fsidx]; /* possibly redundant */ + poll[pdidx].in_flags |= flags; /* possibly redundant */ + break; + } + } + } + +#if 0 + /* Second set the native osfds */ + for (fsidx = 0; fsidx < set->nsize; fsidx++) + { + for (pdidx = 0; ((PRFileDesc*)-1 != poll[pdidx].fd); pdidx++) + { + if ((PRFileDesc*)-1 == poll[pdidx].fd) + { + /* our vector is full - extend and condition it */ + poll = PR_Realloc( + poll, (pdidx + PD_INCR) * sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + memset( + poll + pdidx * sizeof(PRPollDesc), + 0, PD_INCR * sizeof(PRPollDesc)); + poll[(pdidx + PD_INCR)].fd = (PRFileDesc*)-1; + } + if ((NULL == poll[pdidx].fd) + || (poll[pdidx].fd == set->narray[fsidx])) + { + /* either empty or prevously defined */ + poll[pdidx].fd = set->narray[fsidx]; + PR_ASSERT(0 == (poll[pdidx].in_flags & flags)); + poll[pdidx].in_flags |= flags; + break; + } + } + } +#endif /* 0 */ + + return poll; + +out_of_memory: + if (NULL != polldesc) PR_DELETE(polldesc); + return NULL; +} /* _pr_setfd */ + +#endif /* !defined(NEED_SELECT) */ + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + +#if !defined(NEED_SELECT) + PRInt32 npds = 0; + /* + ** Find out how many fds are represented in the three lists. + ** Then allocate a polling descriptor for the logical union + ** (there can't be any overlapping) and call PR_Poll(). + */ + + PRPollDesc *copy, *poll; + + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( "PR_Select()", "PR_Poll()"); + + /* try to get an initial guesss at how much space we need */ + npds = 0; + if ((NULL != pr_rd) && ((pr_rd->hsize + pr_rd->nsize - npds) > 0)) + npds = pr_rd->hsize + pr_rd->nsize; + if ((NULL != pr_wr) && ((pr_wr->hsize + pr_wr->nsize - npds) > 0)) + npds = pr_wr->hsize + pr_wr->nsize; + if ((NULL != pr_ex) && ((pr_ex->hsize + pr_ex->nsize - npds) > 0)) + npds = pr_ex->hsize + pr_ex->nsize; + + if (0 == npds) + { + PR_Sleep(timeout); + return 0; + } + + copy = poll = PR_Calloc(npds + PD_INCR, sizeof(PRPollDesc)); + if (NULL == poll) goto out_of_memory; + poll[npds + PD_INCR - 1].fd = (PRFileDesc*)-1; + + poll = _pr_setfd(pr_rd, PR_POLL_READ, poll); + if (NULL == poll) goto out_of_memory; + poll = _pr_setfd(pr_wr, PR_POLL_WRITE, poll); + if (NULL == poll) goto out_of_memory; + poll = _pr_setfd(pr_ex, PR_POLL_EXCEPT, poll); + if (NULL == poll) goto out_of_memory; + unused = 0; + while (NULL != poll[unused].fd && (PRFileDesc*)-1 != poll[unused].fd) + { + ++unused; + } + + PR_ASSERT(unused > 0); + npds = PR_Poll(poll, unused, timeout); + + if (npds > 0) + { + /* Copy the results back into the fd sets */ + if (NULL != pr_rd) pr_rd->nsize = pr_rd->hsize = 0; + if (NULL != pr_wr) pr_wr->nsize = pr_wr->hsize = 0; + if (NULL != pr_ex) pr_ex->nsize = pr_ex->hsize = 0; + for (copy = &poll[unused - 1]; copy >= poll; --copy) + { + if (copy->out_flags & PR_POLL_NVAL) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + npds = -1; + break; + } + if (copy->out_flags & PR_POLL_READ) + if (NULL != pr_rd) pr_rd->harray[pr_rd->hsize++] = copy->fd; + if (copy->out_flags & PR_POLL_WRITE) + if (NULL != pr_wr) pr_wr->harray[pr_wr->hsize++] = copy->fd; + if (copy->out_flags & PR_POLL_EXCEPT) + if (NULL != pr_ex) pr_ex->harray[pr_ex->hsize++] = copy->fd; + } + } + PR_DELETE(poll); + + return npds; +out_of_memory: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + +#endif /* !defined(NEED_SELECT) */ + +} diff --git a/pr/src/io/prstdio.c b/pr/src/io/prstdio.c new file mode 100644 index 00000000..7c856bef --- /dev/null +++ b/pr/src/io/prstdio.c @@ -0,0 +1,46 @@ +/* -*- 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 <string.h> + +/* +** fprintf to a PRFileDesc +*/ +PR_IMPLEMENT(PRUint32) PR_fprintf(PRFileDesc* fd, const char *fmt, ...) +{ + va_list ap; + PRUint32 rv; + + va_start(ap, fmt); + rv = PR_vfprintf(fd, fmt, ap); + va_end(ap); + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_vfprintf(PRFileDesc* fd, const char *fmt, va_list ap) +{ + /* XXX this could be better */ + PRUint32 rv, len; + char* msg = PR_vsmprintf(fmt, ap); + len = strlen(msg); + rv = PR_Write(fd, msg, len); + PR_DELETE(msg); + return rv; +} diff --git a/pr/src/linking/Makefile b/pr/src/linking/Makefile new file mode 100644 index 00000000..eaef143b --- /dev/null +++ b/pr/src/linking/Makefile @@ -0,0 +1,43 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + prlink.c \ + $(NULL) + +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/linking/prlink.c b/pr/src/linking/prlink.c new file mode 100644 index 00000000..aacc7715 --- /dev/null +++ b/pr/src/linking/prlink.c @@ -0,0 +1,950 @@ +/* -*- 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 <string.h> + +#ifdef XP_MAC +#include <CodeFragments.h> +#include <TextUtils.h> +#include <Types.h> +#include <Strings.h> +#endif + +#ifdef XP_UNIX +#ifdef USE_DLFCN +#include <dlfcn.h> +#elif defined(USE_HPSHL) +#include <dl.h> +#endif + +/* Define this on systems which don't have it (AIX) */ +#ifndef RTLD_LAZY +#define RTLD_LAZY 1 +#endif +#endif /* XP_UNIX */ + +#ifdef XP_PC +typedef PRStaticLinkTable *NODL_PROC(void); +#endif + +/************************************************************************/ + +struct PRLibrary { + char* name; /* Our own copy of the name string */ + PRLibrary* next; + int refCount; + const PRStaticLinkTable* staticTable; + +#ifdef XP_PC + HINSTANCE dlh; +#endif +#ifdef XP_MAC + CFragConnectionID dlh; +#endif + +#ifdef XP_UNIX +#if defined(USE_HPSHL) + shl_t dlh; +#else + void* dlh; +#endif +#endif +}; + +static PRLibrary *pr_loadmap; +static PRLibrary *pr_exe_loadmap; +static PRMonitor *pr_linker_lock; +static char* _pr_currentLibPath = NULL; + +/************************************************************************/ + +#if ( !defined(USE_DLFCN) && !defined(HAVE_STRERROR) ) || defined(HPUX) || defined(BSDI) || defined(LINUX) +static char* errStrBuf = NULL; +#define ERR_STR_BUF_LENGTH 20 +static char* errno_string(PRIntn oserr) +{ + if (errStrBuf == NULL) + errStrBuf = PR_MALLOC(ERR_STR_BUF_LENGTH); + PR_snprintf(errStrBuf, ERR_STR_BUF_LENGTH, "error %ld", oserr); + return errStrBuf; +} +#endif + +static void DLLErrorInternal(PRIntn oserr) +/* +** This whole function, and most of the code in this file, are run +** with a big hairy lock wrapped around it. Not the best of situations, +** but will eventually come up with the right answer. +*/ +{ + const char *error = NULL; +#ifdef HAVE_DLL +# ifdef USE_DLFCN + error = dlerror(); /* $$$ That'll be wrong some of the time - AOF */ +# elif defined(USE_HPSHL) +# ifdef HAVE_STRERROR + error = strerror(oserr); /* This should be okay */ +# else + error = errno_string(oserr); +# endif /* HAVE_STRERROR */ +# else + error = errno_string(oserr); +# endif +#else + error = errno_string(oserr); +#endif /* HAVE_DLL */ + if (NULL != error) + PR_SetErrorText(strlen(error), error); +} /* DLLErrorInternal */ + +void _PR_InitLinker(void) +{ +#ifndef XP_MAC + PRLibrary *lm; +#endif +#if defined(XP_UNIX) + void *h; +#endif + + if (!pr_linker_lock) { + pr_linker_lock = PR_NewNamedMonitor("linker-lock"); + } + PR_EnterMonitor(pr_linker_lock); + +#if defined(XP_PC) + lm = PR_NEWZAP(PRLibrary); + lm->name = strdup("Executable"); + /* + ** In WIN32, GetProcAddress(...) expects a module handle in order to + ** get exported symbols from the executable... + ** + ** However, in WIN16 this is accomplished by passing NULL to + ** GetProcAddress(...) + */ +#if defined(_WIN32) + lm->dlh = GetModuleHandle(NULL); +#else + lm->dlh = (HINSTANCE)NULL; +#endif /* ! _WIN32 */ + + lm->refCount = 1; + lm->staticTable = NULL; + pr_exe_loadmap = lm; + pr_loadmap = lm; + +#elif defined(XP_UNIX) +#ifdef HAVE_DLL +#ifdef USE_DLFCN + h = dlopen(0, RTLD_LAZY); + if (!h) { + char *error; + + DLLErrorInternal(_MD_ERRNO()); + error = (char*)PR_MALLOC(PR_GetErrorTextLength()); + (void) PR_GetErrorText(error); + fprintf(stderr, "failed to initialize shared libraries [%s]\n", + error); + PR_DELETE(error); + abort();/* XXX */ + } +#elif defined(USE_HPSHL) + h = NULL; + /* don't abort with this NULL */ +#else +#error no dll strategy +#endif /* USE_DLFCN */ + + lm = PR_NEWZAP(PRLibrary); + if (lm) { + lm->name = strdup("a.out"); + lm->refCount = 1; + lm->dlh = h; + lm->staticTable = NULL; + } + pr_exe_loadmap = lm; + pr_loadmap = lm; +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + +#ifndef XP_MAC + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (init)", lm?lm->name:"NULL")); +#endif + + PR_ExitMonitor(pr_linker_lock); +} + +#if defined(WIN16) +void _PR_ShutdownLinker(void) +{ + PR_EnterMonitor(pr_linker_lock); + + while (pr_loadmap) { + if (pr_loadmap->refCount > 1) { +#ifdef DEBUG + fprintf(stderr, "# Forcing library to unload: %s (%d outstanding references)\n", + pr_loadmap->name, pr_loadmap->refCount); +#endif + pr_loadmap->refCount = 1; + } + PR_UnloadLibrary(pr_loadmap); + } + + PR_ExitMonitor(pr_linker_lock); + + PR_DestroyMonitor(pr_linker_lock); + pr_linker_lock = NULL; +} +#endif + +/******************************************************************************/ + +PR_IMPLEMENT(PRStatus) PR_SetLibraryPath(const char *path) +{ + PRStatus rv = PR_SUCCESS; + + PR_EnterMonitor(pr_linker_lock); + PR_FREEIF(_pr_currentLibPath); + if (path) { + _pr_currentLibPath = strdup(path); + if (!_pr_currentLibPath) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + rv = PR_FAILURE; + } + } else { + _pr_currentLibPath = 0; + } + PR_ExitMonitor(pr_linker_lock); + return rv; +} + +/* +** Return the library path for finding shared libraries. +*/ +PR_IMPLEMENT(char *) +PR_GetLibraryPath() +{ + char *ev; + char *copy = NULL; /* a copy of _pr_currentLibPath */ + + PR_EnterMonitor(pr_linker_lock); + if (_pr_currentLibPath != NULL) { + goto exit; + } + + /* initialize pr_currentLibPath */ + +#ifdef XP_PC + ev = getenv("LD_LIBRARY_PATH"); + if (!ev) { + ev = ".;\\lib"; + } + ev = strdup(ev); +#endif + +#ifdef XP_MAC + { + char *p; + int len; + + ev = getenv("LD_LIBRARY_PATH"); + + /* if we couldn't find something make up a default */ + if (!ev) + ev = "/usr/local/netscape;/usr/local/netscape/java/bin"; /* do we put the classes in here too? */ + + len = strlen(ev) + 1; /* +1 for the null */ + p = (char*) PR_MALLOC(len); + if (p) { + strcpy(p, ev); + } + ev = p; + } +#endif + +#ifdef XP_UNIX +#if defined USE_DLFCN + { + char *home; + char *local; + char *p; + int len; + + ev = getenv("LD_LIBRARY_PATH"); + if (!ev) { + ev = "/usr/lib:/lib"; + } + home = getenv("HOME"); + + /* + ** Augment the path automatically by adding in ~/.netscape and + ** /usr/local/netscape + */ + len = strlen(ev) + 1; /* +1 for the null */ + if (home && home[0]) { + len += strlen(home) + 1; /* +1 for the colon */ + } + + local = ":/usr/local/netscape/lib/" PR_LINKER_ARCH; + len += strlen(local); /* already got the : */ + p = (char*) PR_MALLOC(len+50); + if (p) { + strcpy(p, ev); + if (home) { + strcat(p, ":"); + strcat(p, home); + } + strcat(p, local); + } + ev = p; + PR_LOG(_pr_io_lm, PR_LOG_NOTICE, ("linker path '%s'", ev)); + } +#else + /* AFAIK there isn't a library path with the HP SHL interface --Rob */ + ev = strdup(""); +#endif +#endif + + /* + * If ev is NULL, we have run out of memory + */ + _pr_currentLibPath = ev; + + exit: + if (_pr_currentLibPath) { + copy = strdup(_pr_currentLibPath); + } + PR_ExitMonitor(pr_linker_lock); + if (!copy) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return copy; +} + +/* +** Build library name from path, lib and extensions +*/ +PR_IMPLEMENT(char*) +PR_GetLibraryName(const char *path, const char *lib) +{ + char *fullname; + +#ifdef XP_PC + if (strstr(lib, PR_DLL_SUFFIX) == NULL) + { + fullname = PR_smprintf("%s\\%s%s", path, lib, PR_DLL_SUFFIX); + } else { + fullname = PR_smprintf("%s\\%s", path, lib); + } +#endif /* XP_PC */ +#ifdef XP_MAC + fullname = PR_smprintf("%s%s", path, lib); +#endif +#ifdef XP_UNIX + if (strstr(lib, PR_DLL_SUFFIX) == NULL) + { + fullname = PR_smprintf("%s/lib%s%s", path, lib, PR_DLL_SUFFIX); + } else { + fullname = PR_smprintf("%s/%s", path, lib); + } +#endif /* XP_UNIX */ + return fullname; +} + +/* +** Free the memory allocated, for the caller, by PR_GetLibraryName +*/ +PR_IMPLEMENT(void) +PR_FreeLibraryName(char *mem) +{ + PR_smprintf_free(mem); +} + +static PRLibrary* +pr_UnlockedFindLibrary(const char *name) +{ + PRLibrary* lm = pr_loadmap; + const char* np = strrchr(name, PR_DIRECTORY_SEPARATOR); + np = np ? np + 1 : name; + while (lm) { + const char* cp = strrchr(lm->name, PR_DIRECTORY_SEPARATOR); + cp = cp ? cp + 1 : lm->name; +#ifdef XP_PC + /* Windows DLL names are case insensitive... */ + if (strcmpi(np, cp) == 0) +#else + if (strcmp(np, cp) == 0) +#endif + { + /* found */ + lm->refCount++; + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s incr => %d (find lib)", + lm->name, lm->refCount)); + return lm; + } + lm = lm->next; + } + return NULL; +} + +/* +** Dynamically load a library. Only load libraries once, so scan the load +** map first. +*/ +PR_IMPLEMENT(PRLibrary*) +PR_LoadLibrary(const char *name) +{ + PRLibrary *lm; + PRLibrary* result; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + /* See if library is already loaded */ + PR_EnterMonitor(pr_linker_lock); + + result = pr_UnlockedFindLibrary(name); + if (result != NULL) goto unlock; + + lm = PR_NEWZAP(PRLibrary); + if (lm == NULL) goto unlock; + lm->staticTable = NULL; + +#ifdef XP_OS2 /* Why isn't all this stuff in MD code?! */ + { + NODL_PROC *pfn; + HMODULE h; + UCHAR pszError[_MAX_PATH]; + ULONG ulRc = NO_ERROR; + int first_try = 1; + + retry: + ulRc = DosLoadModule(pszError, _MAX_PATH, (PSZ) name, &h); + if (ulRc != NO_ERROR) { + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#endif /* XP_OS2 */ + +#if defined(WIN32) || defined(WIN16) + { + HINSTANCE h; + NODL_PROC *pfn; + + h = LoadLibrary(name); + if (h < (HINSTANCE)HINSTANCE_ERROR) { + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + + /* + ** Try to load a table of "static functions" provided by the DLL + */ + + pfn = (NODL_PROC *)GetProcAddress(h, "NODL_TABLE"); + if (pfn != NULL) { + lm->staticTable = (*pfn)(); + } + } +#endif /* WIN32 || WIN16 */ + +#if defined(XP_MAC) && GENERATINGCFM + { + OSErr err; + Ptr main; + CFragConnectionID connectionID; + Str255 errName; + Str255 pName; + char cName[64]; + const char* libName; + + /* + * Algorithm: The "name" passed in could be either a shared + * library name that we should look for in the normal library + * search paths, or a full path name to a specific library on + * disk. Since the full path will always contain a ":" + * (shortest possible path is "Volume:File"), and since a + * library name can not contain a ":", we can test for the + * presence of a ":" to see which type of library we should load. + * or its a full UNIX path which we for now assume is Java + * enumerating all the paths (see below) + */ + if (strchr(name, PR_PATH_SEPARATOR) == NULL) + { + if (strchr(name, PR_DIRECTORY_SEPARATOR) == NULL) + { + /* + * The name did not contain a ":", so it must be a + * library name. Convert the name to a Pascal string + * and try to find the library. + */ + } + else + { + /* name contained a "/" which means we need to suck off the last part */ + /* of the path and pass that on the NSGetSharedLibrary */ + /* this may not be what we really want to do .. because Java could */ + /* be iterating through the whole LD path, and we'll find it if it's */ + /* anywhere on that path -- it appears that's what UNIX and the PC do */ + /* too...so we'll emulate but it could be wrong. */ + name = strrchr(name, PR_DIRECTORY_SEPARATOR) + 1; + } + + PStrFromCStr(name, pName); + + err = NSGetSharedLibrary(pName, &connectionID, &main); + if (err != noErr) + goto unlock; + + libName = name; + } + else + { + /* + * The name did contain a ":", so it must be a full path name. + * Now we have to do a lot of work to convert the path name to + * an FSSpec (silly, since we were probably just called from the + * MacFE plug-in code that already knew the FSSpec and converted + * it to a full path just to pass to us). First we copy out the + * volume name (the text leading up to the first ":"); then we + * separate the file name (the text following the last ":") from + * rest of the path. After converting the strings to Pascal + * format we can call GetCatInfo to get the parent directory ID + * of the file, and then (finally) make an FSSpec and call + * GetDiskFragment. + */ + char* cMacPath = NULL; + char* cFileName = NULL; + char* position = NULL; + CInfoPBRec pb; + FSSpec fileSpec; + PRUint32 index; + + /* Copy the name: we'll change it */ + cMacPath = strdup(name); + if (cMacPath == NULL) + goto unlock; + + /* First, get the vRefNum */ + position = strchr(cMacPath, PR_PATH_SEPARATOR); + if ((position == cMacPath) || (position == NULL)) + fileSpec.vRefNum = 0; /* Use application relative searching */ + else + { + char cVolName[32]; + memset(cVolName, 0, sizeof(cVolName)); + strncpy(cVolName, cMacPath, position-cMacPath); + fileSpec.vRefNum = GetVolumeRefNumFromName(cVolName); + } + + /* Next, break the path and file name apart */ + index = 0; + while (cMacPath[index] != 0) + index++; + while (cMacPath[index] != PR_PATH_SEPARATOR && index > 0) + index--; + if (index == 0 || index == strlen(cMacPath)) + { + PR_DELETE(cMacPath); + goto unlock; + } + cMacPath[index] = 0; + cFileName = &(cMacPath[index + 1]); + + /* Convert the path and name into Pascal strings */ + strcpy((char*) &pName, cMacPath); + c2pstr((char*) &pName); + strcpy((char*) &fileSpec.name, cFileName); + c2pstr((char*) &fileSpec.name); + strcpy(cName, cFileName); + PR_DELETE(cMacPath); + cMacPath = NULL; + + /* Now we can look up the path on the volume */ + pb.dirInfo.ioNamePtr = pName; + pb.dirInfo.ioVRefNum = fileSpec.vRefNum; + pb.dirInfo.ioDrDirID = 0; + pb.dirInfo.ioFDirIndex = 0; + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto unlock; + fileSpec.parID = pb.dirInfo.ioDrDirID; + + /* Finally, try to load the library */ + err = GetDiskFragment(&fileSpec, 0, kCFragGoesToEOF, fileSpec.name, + kLoadCFrag, &connectionID, &main, errName); + + libName = cName; + if (err != noErr) + goto unlock; + } + + lm->name = strdup(libName); + lm->dlh = connectionID; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#elif defined(XP_MAC) && !GENERATINGCFM + { + + } +#endif + +#ifdef XP_UNIX +#ifdef HAVE_DLL + { +#if defined(USE_DLFCN) + void *h = dlopen(name, RTLD_LAZY); +#elif defined(USE_HPSHL) + shl_t h = shl_load(name, BIND_DEFERRED | DYNAMIC_PATH, 0L); +#else +#error Configuration error +#endif + if (!h) { + PR_DELETE(lm); + goto unlock; + } + lm->name = strdup(name); + lm->dlh = h; + lm->next = pr_loadmap; + pr_loadmap = lm; + } +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + + lm->refCount = 1; + result = lm; /* success */ + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (load lib)", lm->name)); + + unlock: + if (result == NULL) { + PR_SetError(PR_LOAD_LIBRARY_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); /* sets error text */ + } + PR_ExitMonitor(pr_linker_lock); + return result; +} + +PR_IMPLEMENT(PRLibrary*) +PR_FindLibrary(const char *name) +{ + PRLibrary* result; + + PR_EnterMonitor(pr_linker_lock); + result = pr_UnlockedFindLibrary(name); + PR_ExitMonitor(pr_linker_lock); + return result; +} + +/* +** Unload a shared library which was loaded via PR_LoadLibrary +*/ +PR_IMPLEMENT(PRStatus) +PR_UnloadLibrary(PRLibrary *lib) +{ + int result = 0; + PRStatus status = PR_SUCCESS; + + if ((lib == 0) || (lib->refCount <= 0)) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + + PR_EnterMonitor(pr_linker_lock); + if (--lib->refCount > 0) { + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s decr => %d", + lib->name, lib->refCount)); + goto done; + } +#ifdef XP_UNIX +#ifdef HAVE_DLL +#ifdef USE_DLFCN + result = dlclose(lib->dlh); +#elif defined(USE_HPSHL) + result = shl_unload(lib->dlh); +#else +#error Configuration error +#endif +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ +#ifdef XP_PC + if (lib->dlh) { + FreeLibrary((HINSTANCE)(lib->dlh)); + lib->dlh = (HINSTANCE)NULL; + } +#endif /* XP_PC */ + +#if defined(XP_MAC) && GENERATINGCFM + /* Close the connection */ + CloseConnection(&(lib->dlh)); +#endif + + /* unlink from library search list */ + if (pr_loadmap == lib) + pr_loadmap = pr_loadmap->next; + else if (pr_loadmap != NULL) { + PRLibrary* prev = pr_loadmap; + PRLibrary* next = pr_loadmap->next; + while (next != NULL) { + if (next == lib) { + prev->next = next->next; + goto freeLib; + } + prev = next; + next = next->next; + } + /* + * fail (the library is not on the _pr_loadmap list), + * but don't wipe out an error from dlclose/shl_unload. + */ + PR_ASSERT(!"_pr_loadmap and lib->refCount inconsistent"); + if (result == 0) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + status = PR_FAILURE; + } + } + /* + * We free the PRLibrary structure whether dlclose/shl_unload + * succeeds or not. + */ + + freeLib: + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Unloaded library %s", lib->name)); + PR_DELETE(lib->name); + PR_DELETE(lib); + if (result == -1) { + PR_SetError(PR_UNLOAD_LIBRARY_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + status = PR_FAILURE; + } + +done: + PR_ExitMonitor(pr_linker_lock); + return status; +} + +static void* +pr_FindSymbolInLib(PRLibrary *lm, const char *name) +{ + void *f = NULL; + + if (lm->staticTable != NULL) { + const PRStaticLinkTable* tp; + for (tp = lm->staticTable; tp->name; tp++) { + if (strcmp(name, tp->name) == 0) { + return (void*) tp->fp; + } + } + /* + ** If the symbol was not found in the static table then check if + ** the symbol was exported in the DLL... Win16 only!! + */ +#if !defined(WIN16) + PR_SetError(PR_FIND_SYMBOL_ERROR, 0); + return (void*)NULL; +#endif + } + +#ifdef XP_OS2 + DosQueryProcAddr(lm->dlh, 0, name, (PFN *) &f); +#endif /* XP_OS2 */ + +#if defined(WIN32) || defined(WIN16) + f = GetProcAddress(lm->dlh, name); +#endif /* WIN32 || WIN16 */ + +#ifdef XP_MAC + { + Ptr symAddr; + CFragSymbolClass symClass; + Str255 pName; + + PStrFromCStr(name, pName); + + f = (NSFindSymbol(lm->dlh, pName, &symAddr, &symClass) == noErr) ? symAddr : NULL; + } +#endif /* XP_MAC */ + +#ifdef XP_UNIX +#ifdef HAVE_DLL +#ifdef USE_DLFCN + f = dlsym(lm->dlh, name); +#elif defined(USE_HPSHL) + if (shl_findsym(&lm->dlh, name, TYPE_PROCEDURE, &f) == -1) + f = NULL; +#endif +#endif /* HAVE_DLL */ +#endif /* XP_UNIX */ + if (f == NULL) { + PR_SetError(PR_FIND_SYMBOL_ERROR, _MD_ERRNO()); + DLLErrorInternal(_MD_ERRNO()); + } + return f; +} + +/* +** Called by class loader to resolve missing native's +*/ +PR_IMPLEMENT(void*) +PR_FindSymbol(PRLibrary *lib, const char *raw_name) +{ + void *f = NULL; +#if defined(SUNOS4) || defined(WIN16) + char *name; +#else + const char *name; +#endif + /* + ** Mangle the raw symbol name in any way that is platform specific. + */ +#if defined(SUNOS4) + /* Need a leading _ */ + name = PR_smprintf("_%s", raw_name); +#elif defined(AIX) + /* + ** AIX with the normal linker put's a "." in front of the symbol + ** name. When use "svcc" and "svld" then the "." disappears. Go + ** figure. + */ + name = raw_name; +#elif defined(WIN16) + /* + ** Win16. symbols have a leading '_' + */ + name = PR_smprintf("_%s", raw_name); +#else + name = raw_name; +#endif + + PR_EnterMonitor(pr_linker_lock); + PR_ASSERT(lib != NULL); + f = pr_FindSymbolInLib(lib, name); + +#if defined(SUNOS4) || defined(WIN16) + PR_smprintf_free(name); +#endif + + PR_ExitMonitor(pr_linker_lock); + return f; +} + +PR_IMPLEMENT(void*) +PR_FindSymbolAndLibrary(const char *raw_name, PRLibrary* *lib) +{ + void *f = NULL; +#if defined(SUNOS4) || defined(WIN16) + char *name; +#else + const char *name; +#endif + PRLibrary* lm; + + /* + ** Mangle the raw symbol name in any way that is platform specific. + */ +#if defined(SUNOS4) + /* Need a leading _ */ + name = PR_smprintf("_%s", raw_name); +#elif defined(AIX) + /* + ** AIX with the normal linker put's a "." in front of the symbol + ** name. When use "svcc" and "svld" then the "." disappears. Go + ** figure. + */ + name = raw_name; +#elif defined(WIN16) + /* + ** Win16. symbols have a leading '_' + */ + name = PR_smprintf("_%s", raw_name); +#else + name = raw_name; +#endif + + PR_EnterMonitor(pr_linker_lock); + + /* search all libraries */ + for (lm = pr_loadmap; lm != NULL; lm = lm->next) { + f = pr_FindSymbolInLib(lm, name); + if (f != NULL) { + *lib = lm; + lm->refCount++; + PR_LOG(_pr_linker_lm, PR_LOG_MIN, + ("%s incr => %d (for %s)", + lm->name, lm->refCount, name)); + break; + } + } +#if defined(SUNOS4) || defined(WIN16) + PR_smprintf_free(name); +#endif + + PR_ExitMonitor(pr_linker_lock); + return f; +} + +/* +** Add a static library to the list of loaded libraries. If LoadLibrary +** is called with the name then we will pretend it was already loaded +*/ +PR_IMPLEMENT(PRLibrary*) +PR_LoadStaticLibrary(const char *name, const PRStaticLinkTable *slt) +{ + PRLibrary *lm; + PRLibrary* result = NULL; + + /* See if library is already loaded */ + PR_EnterMonitor(pr_linker_lock); + + /* If the lbrary is already loaded, then add the static table information... */ + result = pr_UnlockedFindLibrary(name); + if (result != NULL) { + PR_ASSERT( (result->staticTable == NULL) || (result->staticTable == slt) ); + result->staticTable = slt; + goto unlock; + } + + /* Add library to list...Mark it static */ + lm = PR_NEWZAP(PRLibrary); + if (lm == NULL) goto unlock; + + lm->name = strdup(name); + lm->refCount = 1; + lm->dlh = pr_exe_loadmap ? pr_exe_loadmap->dlh : 0; + lm->staticTable = slt; + lm->next = pr_loadmap; + pr_loadmap = lm; + + result = lm; /* success */ + PR_ASSERT(lm->refCount == 1); + unlock: + PR_LOG(_pr_linker_lm, PR_LOG_MIN, ("Loaded library %s (static lib)", lm->name)); + PR_ExitMonitor(pr_linker_lock); + return result; +} diff --git a/pr/src/malloc/Makefile b/pr/src/malloc/Makefile new file mode 100644 index 00000000..62a2e17f --- /dev/null +++ b/pr/src/malloc/Makefile @@ -0,0 +1,41 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(DIST)/include/private -I$(DIST)/include + +CSRCS = prmalloc.c prmem.c + +include $(MOD_DEPTH)/config/rules.mk + +export:: $(TARGETS) + +install:: export diff --git a/pr/src/malloc/prmalloc.c b/pr/src/malloc/prmalloc.c new file mode 100644 index 00000000..b7357462 --- /dev/null +++ b/pr/src/malloc/prmalloc.c @@ -0,0 +1,1156 @@ +/* -*- 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" + +/* +** We override malloc etc. on any platform which has preemption + +** nspr20 user level threads. When we're debugging, we can make our +** version of malloc fail occasionally. +*/ +#ifdef _PR_OVERRIDE_MALLOC + +/* +** Thread safe version of malloc, calloc, realloc, free +*/ +#include <stdarg.h> + +#ifdef DEBUG +#define SANITY +#define EXTRA_SANITY +#else +#undef SANITY +#undef EXTRA_SANITY +#endif + +/* Forward decls */ +void *_PR_UnlockedMalloc(size_t size); +void _PR_UnlockedFree(void *ptr); +void *_PR_UnlockedRealloc(void *ptr, size_t size); +void *_PR_UnlockedCalloc(size_t n, size_t elsize); + +/************************************************************************/ + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + * + */ + +/* + * Defining SANITY will enable some checks which will tell you if the users + * program did botch something + */ + +/* + * Defining EXTRA_SANITY will enable some checks which are mostly related + * to internal conditions in malloc.c + */ + +/* + * Very verbose progress on stdout... + */ +#if 0 +# define TRACE(foo) printf foo +static int malloc_event; +#else +# define TRACE(foo) +#endif + +/* XXX Pick a number, any number */ +# define malloc_pagesize 4096UL +# define malloc_pageshift 12UL + +#ifdef XP_UNIX +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <memory.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/mman.h> +#endif + +/* + * This structure describes a page's worth of chunks. + */ + +struct pginfo { + struct pginfo *next; /* next on the free list */ + char *page; /* Pointer to the page */ + u_short size; /* size of this page's chunks */ + u_short shift; /* How far to shift for this size chunks */ + u_short free; /* How many free chunks */ + u_short total; /* How many chunk */ + u_long bits[1]; /* Which chunks are free */ +}; + +struct pgfree { + struct pgfree *next; /* next run of free pages */ + struct pgfree *prev; /* prev run of free pages */ + char *page; /* pointer to free pages */ + char *end; /* pointer to end of free pages */ + u_long size; /* number of bytes free */ +}; + +/* + * How many bits per u_long in the bitmap. + * Change only if not 8 bits/byte + */ +#define MALLOC_BITS (8*sizeof(u_long)) + +/* + * Magic values to put in the page_directory + */ +#define MALLOC_NOT_MINE ((struct pginfo*) 0) +#define MALLOC_FREE ((struct pginfo*) 1) +#define MALLOC_FIRST ((struct pginfo*) 2) +#define MALLOC_FOLLOW ((struct pginfo*) 3) +#define MALLOC_MAGIC ((struct pginfo*) 4) + +/* + * Set to one when malloc_init has been called + */ +static unsigned initialized; + +/* + * The size of a page. + * Must be a integral multiplum of the granularity of mmap(2). + * Your toes will curl if it isn't a power of two + */ +#define malloc_pagemask ((malloc_pagesize)-1) + +/* + * The size of the largest chunk. + * Half a page. + */ +#define malloc_maxsize ((malloc_pagesize)>>1) + +/* + * malloc_pagesize == 1 << malloc_pageshift + */ +#ifndef malloc_pageshift +static unsigned malloc_pageshift; +#endif /* malloc_pageshift */ + +/* + * The smallest allocation we bother about. + * Must be power of two + */ +#ifndef malloc_minsize +static unsigned malloc_minsize; +#endif /* malloc_minsize */ + +/* + * The largest chunk we care about. + * Must be smaller than pagesize + * Must be power of two + */ +#ifndef malloc_maxsize +static unsigned malloc_maxsize; +#endif /* malloc_maxsize */ + +#ifndef malloc_cache +static unsigned malloc_cache; +#endif /* malloc_cache */ + +/* + * The offset from pagenumber to index into the page directory + */ +static u_long malloc_origo; + +/* + * The last index in the page directory we care about + */ +static u_long last_index; + +/* + * Pointer to page directory. + * Allocated "as if with" malloc + */ +static struct pginfo **page_dir; + +/* + * How many slots in the page directory + */ +static unsigned malloc_ninfo; + +/* + * Free pages line up here + */ +static struct pgfree free_list; + +/* + * Abort() if we fail to get VM ? + */ +static int malloc_abort; + +#ifdef SANITY +/* + * Are we trying to die ? + */ +static int suicide; +#endif + +/* + * dump statistics + */ +static int malloc_stats; + +/* + * always realloc ? + */ +static int malloc_realloc; + +/* + * my last break. + */ +static void *malloc_brk; + +/* + * one location cache for free-list holders + */ +static struct pgfree *px; + +static int set_pgdir(void *ptr, struct pginfo *info); +static int extend_page_directory(u_long index); + +#ifdef SANITY +void +malloc_dump(FILE *fd) +{ + struct pginfo **pd; + struct pgfree *pf; + int j; + + pd = page_dir; + + /* print out all the pages */ + for(j=0;j<=last_index;j++) { + fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); + if (pd[j] == MALLOC_NOT_MINE) { + for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++) + ; + j--; + fprintf(fd,".. %5d not mine\n", j); + } else if (pd[j] == MALLOC_FREE) { + for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++) + ; + j--; + fprintf(fd,".. %5d free\n", j); + } else if (pd[j] == MALLOC_FIRST) { + for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++) + ; + j--; + fprintf(fd,".. %5d in use\n", j); + } else if (pd[j] < MALLOC_MAGIC) { + fprintf(fd,"(%p)\n", pd[j]); + } else { + fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", + pd[j],pd[j]->free, pd[j]->total, + pd[j]->size, pd[j]->page, pd[j]->next); + } + } + + for(pf=free_list.next; pf; pf=pf->next) { + fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", + pf,pf->page,pf->end,pf->size,pf->prev,pf->next); + if (pf == pf->next) { + fprintf(fd,"Free_list loops.\n"); + break; + } + } + + /* print out various info */ + fprintf(fd,"Minsize\t%d\n",malloc_minsize); + fprintf(fd,"Maxsize\t%d\n",malloc_maxsize); + fprintf(fd,"Pagesize\t%d\n",malloc_pagesize); + fprintf(fd,"Pageshift\t%d\n",malloc_pageshift); + fprintf(fd,"FirstPage\t%ld\n",malloc_origo); + fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, + (last_index + malloc_pageshift) << malloc_pageshift); + fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift); +} + +static void wrterror(char *fmt, ...) +{ + char *q = "malloc() error: "; + char buf[100]; + va_list ap; + + suicide = 1; + + va_start(ap, fmt); + PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fputs(q, stderr); + fputs(buf, stderr); + + malloc_dump(stderr); + PR_Abort(); +} + +static void wrtwarning(char *fmt, ...) +{ + char *q = "malloc() warning: "; + char buf[100]; + va_list ap; + + va_start(ap, fmt); + PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + fputs(q, stderr); + fputs(buf, stderr); +} +#endif /* SANITY */ + + +/* + * Allocate a number of pages from the OS + */ +static caddr_t +map_pages(int pages, int update) +{ + caddr_t result,tail; + + result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; + result = (caddr_t) ((u_long)result & ~malloc_pagemask); + tail = result + (pages << malloc_pageshift); + if (!brk(tail)) { + last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; + malloc_brk = tail; + TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); + if (!update || last_index < malloc_ninfo || + extend_page_directory(last_index)) + return result; + } + TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno)); +#ifdef EXTRA_SANITY + wrterror("map_pages fails\n"); +#endif + return 0; +} + +#define set_bit(_pi,_bit) \ + (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS) + +#define clr_bit(_pi,_bit) \ + (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS)); + +#define tst_bit(_pi,_bit) \ + ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS))) + +/* + * Extend page directory + */ +static int +extend_page_directory(u_long index) +{ + struct pginfo **new,**old; + int i; + + TRACE(("%6d E %lu\n",malloc_event++,index)); + + /* Make it this many pages */ + i = index * sizeof *page_dir; + i /= malloc_pagesize; + i += 2; + + /* Get new pages, if you used this much mem you don't care :-) */ + new = (struct pginfo**) map_pages(i,0); + if (!new) + return 0; + + /* Copy the old stuff */ + memset(new, 0, i * malloc_pagesize); + memcpy(new, page_dir, + malloc_ninfo * sizeof *page_dir); + + /* register the new size */ + malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; + + /* swap the pointers */ + old = page_dir; + page_dir = new; + + /* Mark the pages */ + index = ((u_long)new >> malloc_pageshift) - malloc_origo; + page_dir[index] = MALLOC_FIRST; + while (--i) { + page_dir[++index] = MALLOC_FOLLOW; + } + + /* Now free the old stuff */ + _PR_UnlockedFree(old); + return 1; +} + +/* + * Set entry in page directory. + * Extend page directory if need be. + */ +static int +set_pgdir(void *ptr, struct pginfo *info) +{ + u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; + + if (index >= malloc_ninfo && !extend_page_directory(index)) + return 0; + page_dir[index] = info; + return 1; +} + +/* + * Initialize the world + */ +static void +malloc_init (void) +{ + int i; + char *p; + + TRACE(("%6d I\n",malloc_event++)); +#ifdef DEBUG + for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { + switch (*p) { + case 'a': malloc_abort = 0; break; + case 'A': malloc_abort = 1; break; + case 'd': malloc_stats = 0; break; + case 'D': malloc_stats = 1; break; + case 'r': malloc_realloc = 0; break; + case 'R': malloc_realloc = 1; break; + default: + wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); + break; + } + } +#endif + +#ifndef malloc_pagesize + /* determine our pagesize */ + malloc_pagesize = getpagesize(); +#endif /* malloc_pagesize */ + +#ifndef malloc_pageshift + /* determine how much we shift by to get there */ + for (i = malloc_pagesize; i > 1; i >>= 1) + malloc_pageshift++; +#endif /* malloc_pageshift */ + +#ifndef malloc_cache + malloc_cache = 50 << malloc_pageshift; +#endif /* malloc_cache */ + +#ifndef malloc_minsize + /* + * find the smallest size allocation we will bother about. + * this is determined as the smallest allocation that can hold + * it's own pginfo; + */ + i = 2; + for(;;) { + int j; + + /* Figure out the size of the bits */ + j = malloc_pagesize/i; + j /= 8; + if (j < sizeof(u_long)) + j = sizeof (u_long); + if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) + break; + i += i; + } + malloc_minsize = i; +#endif /* malloc_minsize */ + + + /* Allocate one page for the page directory */ + page_dir = (struct pginfo **) map_pages(1,0); +#ifdef SANITY + if (!page_dir) + wrterror("fatal: my first mmap failed. (check limits ?)\n"); +#endif + + /* + * We need a maximum of malloc_pageshift buckets, steal these from the + * front of the page_directory; + */ + malloc_origo = (u_long) page_dir >> malloc_pageshift; + malloc_origo -= malloc_pageshift; + + /* Clear it */ + memset(page_dir,0,malloc_pagesize); + + /* Find out how much it tells us */ + malloc_ninfo = malloc_pagesize / sizeof *page_dir; + + /* Plug the page directory into itself */ + i = set_pgdir(page_dir,MALLOC_FIRST); +#ifdef SANITY + if (!i) + wrterror("fatal: couldn't set myself in the page directory\n"); +#endif + + /* Been here, done that */ + initialized++; +} + +/* + * Allocate a number of complete pages + */ +static void *malloc_pages(size_t size) +{ + void *p,*delay_free = 0; + int i; + struct pgfree *pf; + u_long index; + + /* How many pages ? */ + size += (malloc_pagesize-1); + size &= ~malloc_pagemask; + + p = 0; + /* Look for free pages before asking for more */ + for(pf = free_list.next; pf; pf = pf->next) { +#ifdef EXTRA_SANITY + if (pf->page == pf->end) + wrterror("zero entry on free_list\n"); + if (pf->page > pf->end) { + TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++, + pf,pf->page,pf->end,__LINE__)); + wrterror("sick entry on free_list\n"); + } + if ((void*)pf->page >= (void*)sbrk(0)) + wrterror("entry on free_list past brk\n"); + if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] + != MALLOC_FREE) { + TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++, + pf,pf->page,pf->end,__LINE__)); + wrterror("non-free first page on free-list\n"); + } + if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] + != MALLOC_FREE) + wrterror("non-free last page on free-list\n"); +#endif /* EXTRA_SANITY */ + if (pf->size < size) + continue; + else if (pf->size == size) { + p = pf->page; + if (pf->next) + pf->next->prev = pf->prev; + pf->prev->next = pf->next; + delay_free = pf; + break; + } else { + p = pf->page; + pf->page += size; + pf->size -= size; + break; + } + } +#ifdef EXTRA_SANITY + if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] + != MALLOC_FREE) { + wrterror("allocated non-free page on free-list\n"); + } +#endif /* EXTRA_SANITY */ + + size >>= malloc_pageshift; + + /* Map new pages */ + if (!p) + p = map_pages(size,1); + + if (p) { + /* Mark the pages in the directory */ + index = ((u_long)p >> malloc_pageshift) - malloc_origo; + page_dir[index] = MALLOC_FIRST; + for (i=1;i<size;i++) + page_dir[index+i] = MALLOC_FOLLOW; + } + if (delay_free) { + if (!px) + px = delay_free; + else + _PR_UnlockedFree(delay_free); + } + return p; +} + +/* + * Allocate a page of fragments + */ + +static int +malloc_make_chunks(int bits) +{ + struct pginfo *bp; + void *pp; + int i,k,l; + + /* Allocate a new bucket */ + pp = malloc_pages(malloc_pagesize); + if (!pp) + return 0; + l = sizeof *bp - sizeof(u_long); + l += sizeof(u_long) * + (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS); + if ((1<<(bits)) <= l+l) { + bp = (struct pginfo *)pp; + } else { + bp = (struct pginfo *)_PR_UnlockedMalloc(l); + } + if (!bp) + return 0; + bp->size = (1<<bits); + bp->shift = bits; + bp->total = bp->free = malloc_pagesize >> bits; + bp->next = page_dir[bits]; + bp->page = pp; + i = set_pgdir(pp,bp); + if (!i) + return 0; + + /* We can safely assume that there is nobody in this chain */ + page_dir[bits] = bp; + + /* set all valid bits in the bits */ + k = bp->total; + i = 0; +/* + for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) + bp->bits[i / MALLOC_BITS] = ~0; +*/ + for(; i < k; i++) + set_bit(bp,i); + + if (bp != pp) + return 1; + + /* We may have used the first ones already */ + for(i=0;l > 0;i++) { + clr_bit(bp,i); + bp->free--; + bp->total--; + l -= (1 << bits); + } + return 1; +} + +/* + * Allocate a fragment + */ +static void *malloc_bytes(size_t size) +{ + size_t s; + int j; + struct pginfo *bp; + int k; + u_long *lp, bf; + + /* Don't bother with anything less than this */ + if (size < malloc_minsize) { + size = malloc_minsize; + } + + /* Find the right bucket */ + j = 1; + s = size - 1; + while (s >>= 1) { + j++; + } + + /* If it's empty, make a page more of that size chunks */ + if (!page_dir[j] && !malloc_make_chunks(j)) + return 0; + + /* Find first word of bitmap which isn't empty */ + bp = page_dir[j]; + for (lp = bp->bits; !*lp; lp++) + ; + + /* Find that bit */ + bf = *lp; + k = 0; + while ((bf & 1) == 0) { + bf >>= 1; + k++; + } + + *lp ^= 1L<<k; /* clear it */ + bp->free--; + if (!bp->free) { + page_dir[j] = bp->next; + bp->next = 0; + } + k += (lp - bp->bits)*MALLOC_BITS; + return bp->page + (k << bp->shift); +} + +void *_PR_UnlockedMalloc(size_t size) +{ + void *result; + + /* Round up to a multiple of 8 bytes */ + if (size & 7) { + size = size + 8 - (size & 7); + } + + if (!initialized) + malloc_init(); + +#ifdef SANITY + if (suicide) + PR_Abort(); +#endif + + if (size <= malloc_maxsize) + result = malloc_bytes(size); + else + result = malloc_pages(size); +#ifdef SANITY + if (malloc_abort && !result) + wrterror("malloc() returns NULL\n"); +#endif + TRACE(("%6d M %p %d\n",malloc_event++,result,size)); + + return result; +} + +void *_PR_UnlockedMemalign(size_t alignment, size_t size) +{ + void *result; + + /* + * alignment has to be a power of 2 + */ + + if ((size <= alignment) && (alignment <= malloc_maxsize)) + size = alignment; + else + size += alignment - 1; + + /* Round up to a multiple of 8 bytes */ + if (size & 7) { + size = size + 8 - (size & 7); + } + + if (!initialized) + malloc_init(); + +#ifdef SANITY + if (suicide) + abort(); +#endif + + if (size <= malloc_maxsize) + result = malloc_bytes(size); + else + result = malloc_pages(size); +#ifdef SANITY + if (malloc_abort && !result) + wrterror("malloc() returns NULL\n"); +#endif + TRACE(("%6d A %p %d\n",malloc_event++,result,size)); + + if ((u_long)result & (alignment - 1)) + return ((void *)(((u_long)result + alignment) & ~(alignment - 1))); + else + return result; +} + +void *_PR_UnlockedCalloc(size_t n, size_t nelem) +{ + void *p; + + /* Compute total size and then round up to a double word amount */ + n *= nelem; + if (n & 7) { + n = n + 8 - (n & 7); + } + + /* Get the memory */ + p = _PR_UnlockedMalloc(n); + if (p) { + /* Zero it */ + memset(p, 0, n); + } + return p; +} + +/* + * Change an allocation's size + */ +void *_PR_UnlockedRealloc(void *ptr, size_t size) +{ + void *p; + u_long osize,page,index,tmp_index; + struct pginfo **mp; + + if (!initialized) + malloc_init(); + +#ifdef SANITY + if (suicide) + PR_Abort(); +#endif + + /* used as free() */ + TRACE(("%6d R %p %d\n",malloc_event++, ptr, size)); + if (ptr && !size) { + _PR_UnlockedFree(ptr); + return _PR_UnlockedMalloc (1); + } + + /* used as malloc() */ + if (!ptr) { + p = _PR_UnlockedMalloc(size); + return p; + } + + /* Find the page directory entry for the page in question */ + page = (u_long)ptr >> malloc_pageshift; + index = page - malloc_origo; + + /* + * check if memory was allocated by memalign + */ + tmp_index = index; + while (page_dir[tmp_index] == MALLOC_FOLLOW) + tmp_index--; + if (tmp_index != index) { + /* + * memalign-allocated memory + */ + index = tmp_index; + page = index + malloc_origo; + ptr = (void *) (page << malloc_pageshift); + } + TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size)); + + /* make sure it makes sense in some fashion */ + if (index < malloc_pageshift || index > last_index) { +#ifdef SANITY + wrtwarning("junk pointer passed to realloc()\n"); +#endif + return 0; + } + + /* find the size of that allocation, and see if we need to relocate */ + mp = &page_dir[index]; + if (*mp == MALLOC_FIRST) { + osize = malloc_pagesize; + while (mp[1] == MALLOC_FOLLOW) { + osize += malloc_pagesize; + mp++; + } + if (!malloc_realloc && + size < osize && + size > malloc_maxsize && + size > (osize - malloc_pagesize)) { + return ptr; + } + } else if (*mp >= MALLOC_MAGIC) { + osize = (*mp)->size; + if (!malloc_realloc && + size < osize && + (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) { + return ptr; + } + } else { +#ifdef SANITY + wrterror("realloc() of wrong page.\n"); +#endif + } + + /* try to reallocate */ + p = _PR_UnlockedMalloc(size); + + if (p) { + /* copy the lesser of the two sizes */ + if (osize < size) + memcpy(p,ptr,osize); + else + memcpy(p,ptr,size); + _PR_UnlockedFree(ptr); + } +#ifdef DEBUG + else if (malloc_abort) + wrterror("realloc() returns NULL\n"); +#endif + + return p; +} + +/* + * Free a sequence of pages + */ + +static void +free_pages(char *ptr, u_long page, int index, struct pginfo *info) +{ + int i; + struct pgfree *pf,*pt; + u_long l; + char *tail; + + TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page)); + /* Is it free already ? */ + if (info == MALLOC_FREE) { +#ifdef SANITY + wrtwarning("freeing free page at %p.\n", ptr); +#endif + return; + } + +#ifdef SANITY + /* Is it not the right place to begin ? */ + if (info != MALLOC_FIRST) + wrterror("freeing wrong page.\n"); + + /* Is this really a pointer to a page ? */ + if ((u_long)ptr & malloc_pagemask) + wrterror("freeing messed up page pointer.\n"); +#endif + + /* Count how many pages it is anyway */ + page_dir[index] = MALLOC_FREE; + for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) + page_dir[index + i] = MALLOC_FREE; + + l = i << malloc_pageshift; + + tail = ptr+l; + + /* add to free-list */ + if (!px) + px = _PR_UnlockedMalloc(sizeof *pt); + /* XXX check success */ + px->page = ptr; + px->end = tail; + px->size = l; + if (!free_list.next) { + px->next = free_list.next; + px->prev = &free_list; + free_list.next = px; + pf = px; + px = 0; + } else { + tail = ptr+l; + for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next) + ; + for(; pf; pf = pf->next) { + if (pf->end == ptr ) { + /* append to entry */ + pf->end += l; + pf->size += l; + if (pf->next && pf->end == pf->next->page ) { + pt = pf->next; + pf->end = pt->end; + pf->size += pt->size; + pf->next = pt->next; + if (pf->next) + pf->next->prev = pf; + _PR_UnlockedFree(pt); + } + } else if (pf->page == tail) { + /* prepend to entry */ + pf->size += l; + pf->page = ptr; + } else if (pf->page > ptr) { + px->next = pf; + px->prev = pf->prev; + pf->prev = px; + px->prev->next = px; + pf = px; + px = 0; + } else if (!pf->next) { + px->next = 0; + px->prev = pf; + pf->next = px; + pf = px; + px = 0; + } else { + continue; + } + break; + } + } + if (!pf->next && + pf->size > malloc_cache && + pf->end == malloc_brk && + malloc_brk == (void*)sbrk(0)) { + pf->end = pf->page + malloc_cache; + pf->size = malloc_cache; + TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page)); + brk(pf->end); + malloc_brk = pf->end; + /* Find the page directory entry for the page in question */ + page = (u_long)pf->end >> malloc_pageshift; + index = page - malloc_origo; + /* Now update the directory */ + for(i=index;i <= last_index;) + page_dir[i++] = MALLOC_NOT_MINE; + last_index = index - 1; + } +} + +/* + * Free a chunk, and possibly the page it's on, if the page becomes empty. + */ + +static void +free_bytes(void *ptr, u_long page, int index, struct pginfo *info) +{ + int i; + struct pginfo **mp; + void *vp; + + /* Make sure that pointer is multiplum of chunk-size */ +#ifdef SANITY + if ((u_long)ptr & (info->size - 1)) + wrterror("freeing messed up chunk pointer\n"); +#endif + + /* Find the chunk number on the page */ + i = ((u_long)ptr & malloc_pagemask) >> info->shift; + + /* See if it's free already */ + if (tst_bit(info,i)) { +#ifdef SANITY + wrtwarning("freeing free chunk at %p\n", ptr); +#endif + return; + } + + /* Mark it free */ + set_bit(info,i); + info->free++; + + /* If the page was full before, we need to put it on the queue now */ + if (info->free == 1) { + mp = page_dir + info->shift; + while (*mp && (*mp)->next && (*mp)->next->page < info->page) + mp = &(*mp)->next; + info->next = *mp; + *mp = info; + return; + } + + /* If this page isn't empty, don't do anything. */ + if (info->free != info->total) + return; + + /* We may want to keep at least one page of each size chunks around. */ + mp = page_dir + info->shift; + if (0 && (*mp == info) && !info->next) + return; + + /* Find & remove this page in the queue */ + while (*mp != info) { + mp = &((*mp)->next); +#ifdef EXTRA_SANITY + if (!*mp) { + TRACE(("%6d !q %p\n",malloc_event++,info)); + wrterror("Not on queue\n"); + } +#endif + } + *mp = info->next; + + /* Free the page & the info structure if need be */ + set_pgdir(info->page,MALLOC_FIRST); + if((void*)info->page == (void*)info) { + _PR_UnlockedFree(info->page); + } else { + vp = info->page; + _PR_UnlockedFree(info); + _PR_UnlockedFree(vp); + } +} + +void _PR_UnlockedFree(void *ptr) +{ + u_long page; + struct pginfo *info; + int index, tmp_index; + + TRACE(("%6d F %p\n",malloc_event++,ptr)); + /* This is legal */ + if (!ptr) + return; + +#ifdef SANITY + /* There wouldn't be anything to free */ + if (!initialized) { + wrtwarning("free() called before malloc() ever got called\n"); + return; + } +#endif + +#ifdef SANITY + if (suicide) + PR_Abort(); +#endif + + /* Find the page directory entry for the page in question */ + page = (u_long)ptr >> malloc_pageshift; + index = page - malloc_origo; + + /* + * check if memory was allocated by memalign + */ + tmp_index = index; + while (page_dir[tmp_index] == MALLOC_FOLLOW) + tmp_index--; + if (tmp_index != index) { + /* + * memalign-allocated memory + */ + index = tmp_index; + page = index + malloc_origo; + ptr = (void *) (page << malloc_pageshift); + } + /* make sure it makes sense in some fashion */ + if (index < malloc_pageshift) { +#ifdef SANITY + wrtwarning("junk pointer %p (low) passed to free()\n", ptr); +#endif + return; + } + if (index > last_index) { +#ifdef SANITY + wrtwarning("junk pointer %p (high) passed to free()\n", ptr); +#endif + return; + } + + /* handle as page-allocation or chunk allocation */ + info = page_dir[index]; + if (info < MALLOC_MAGIC) + free_pages(ptr,page,index,info); + else + free_bytes(ptr,page,index,info); + return; +} +#endif /* _PR_OVERRIDE_MALLOC */ diff --git a/pr/src/malloc/prmem.c b/pr/src/malloc/prmem.c new file mode 100644 index 00000000..c9319216 --- /dev/null +++ b/pr/src/malloc/prmem.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. + */ + +/* +** Thread safe versions of malloc, free, realloc, calloc and cfree. +*/ + +#include "primpl.h" + +/* +** The PR_Malloc, PR_Calloc, PR_Realloc, and PR_Free functions simply +** call their libc equivalents now. This may seem redundant, but it +** ensures that we are calling into the same runtime library. On +** Win32, it is possible to have multiple runtime libraries (e.g., +** objects compiled with /MD and /MDd) in the same process, and +** they maintain separate heaps, which cannot be mixed. +*/ +PR_IMPLEMENT(void *) PR_Malloc(PRUint32 size) +{ +#if defined (WIN16) + return PR_MD_malloc( (size_t) size); +#else + return malloc(size); +#endif +} + +PR_IMPLEMENT(void *) PR_Calloc(PRUint32 nelem, PRUint32 elsize) +{ +#if defined (WIN16) + return PR_MD_calloc( (size_t)nelem, (size_t)elsize ); + +#else + return calloc(nelem, elsize); +#endif +} + +PR_IMPLEMENT(void *) PR_Realloc(void *ptr, PRUint32 size) +{ +#if defined (WIN16) + return PR_MD_realloc( ptr, (size_t) size); +#else + return realloc(ptr, size); +#endif +} + +PR_IMPLEMENT(void) PR_Free(void *ptr) +{ +#if defined (WIN16) + PR_MD_free( ptr ); +#else + free(ptr); +#endif +} + +/* +** Complexity alert! +** +** If malloc/calloc/free (etc.) were implemented to use pr lock's then +** the entry points could block when called if some other thread had the +** lock. +** +** Most of the time this isn't a problem. However, in the case that we +** are using the thread safe malloc code after PR_Init but before +** PR_AttachThread has been called (on a native thread that nspr has yet +** to be told about) we could get royally screwed if the lock was busy +** and we tried to context switch the thread away. In this scenario +** PR_CURRENT_THREAD() == NULL +** +** To avoid this unfortunate case, we use the low level locking +** facilities for malloc protection instead of the slightly higher level +** locking. This makes malloc somewhat faster so maybe it's a good thing +** anyway. +*/ +#ifdef _PR_OVERRIDE_MALLOC + +/* Imports */ +extern void *_PR_UnlockedMalloc(size_t size); +extern void *_PR_UnlockedMemalign(size_t alignment, size_t size); +extern void _PR_UnlockedFree(void *ptr); +extern void *_PR_UnlockedRealloc(void *ptr, size_t size); +extern void *_PR_UnlockedCalloc(size_t n, size_t elsize); + +static PRBool _PR_malloc_initialised = PR_FALSE; + +#ifdef _PR_PTHREADS +static pthread_mutex_t _PR_MD_malloc_crustylock; + +#define _PR_Lock_Malloc() { \ + if(PR_TRUE == _PR_malloc_initialised) { \ + PRStatus rv; \ + rv = pthread_mutex_lock(&_PR_MD_malloc_crustylock); \ + PR_ASSERT(0 == rv); \ + } + +#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \ + PRStatus rv; \ + rv = pthread_mutex_unlock(&_PR_MD_malloc_crustylock); \ + PR_ASSERT(0 == rv); \ + } \ + } +#else /* _PR_PTHREADS */ +static _MDLock _PR_MD_malloc_crustylock; + +#define _PR_Lock_Malloc() { \ + PRIntn _is; \ + if(PR_TRUE == _PR_malloc_initialised) { \ + if (_PR_MD_CURRENT_THREAD() && \ + !_PR_IS_NATIVE_THREAD( \ + _PR_MD_CURRENT_THREAD())) \ + _PR_INTSOFF(_is); \ + _PR_MD_LOCK(&_PR_MD_malloc_crustylock); \ + } + +#define _PR_Unlock_Malloc() if(PR_TRUE == _PR_malloc_initialised) { \ + _PR_MD_UNLOCK(&_PR_MD_malloc_crustylock); \ + if (_PR_MD_CURRENT_THREAD() && \ + !_PR_IS_NATIVE_THREAD( \ + _PR_MD_CURRENT_THREAD())) \ + _PR_INTSON(_is); \ + } \ + } +#endif /* _PR_PTHREADS */ + +PR_IMPLEMENT(PRStatus) _PR_MallocInit(void) +{ + PRStatus rv = PR_SUCCESS; + + if( PR_TRUE == _PR_malloc_initialised ) return PR_SUCCESS; + +#ifdef _PR_PTHREADS + { + int status; + pthread_mutexattr_t mattr; + + status = PTHREAD_MUTEXATTR_INIT(&mattr); + PR_ASSERT(0 == status); + status = PTHREAD_MUTEX_INIT(_PR_MD_malloc_crustylock, mattr); + PR_ASSERT(0 == status); + status = PTHREAD_MUTEXATTR_DESTROY(&mattr); + PR_ASSERT(0 == status); + } +#else /* _PR_PTHREADS */ + _MD_NEW_LOCK(&_PR_MD_malloc_crustylock); +#endif /* _PR_PTHREADS */ + + if( PR_SUCCESS == rv ) + { + _PR_malloc_initialised = PR_TRUE; + } + + return rv; +} + +void *malloc(size_t size) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedMalloc(size); + _PR_Unlock_Malloc(); + return p; +} + +#if defined(IRIX) +void *memalign(size_t alignment, size_t size) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedMemalign(alignment, size); + _PR_Unlock_Malloc(); + return p; +} + +void *valloc(size_t size) +{ + return(memalign(sysconf(_SC_PAGESIZE),size)); +} +#endif /* IRIX */ + +void free(void *ptr) +{ + _PR_Lock_Malloc(); + _PR_UnlockedFree(ptr); + _PR_Unlock_Malloc(); +} + +void *realloc(void *ptr, size_t size) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedRealloc(ptr, size); + _PR_Unlock_Malloc(); + return p; +} + +void *calloc(size_t n, size_t elsize) +{ + void *p; + _PR_Lock_Malloc(); + p = _PR_UnlockedCalloc(n, elsize); + _PR_Unlock_Malloc(); + return p; +} + +void cfree(void *p) +{ + _PR_Lock_Malloc(); + _PR_UnlockedFree(p); + _PR_Unlock_Malloc(); +} + +void _PR_InitMem(void) +{ + PRStatus rv; + rv = _PR_MallocInit(); + PR_ASSERT(PR_SUCCESS == rv); +} + +#endif /* _PR_OVERRIDE_MALLOC */ diff --git a/pr/src/md/Makefile b/pr/src/md/Makefile new file mode 100644 index 00000000..92460077 --- /dev/null +++ b/pr/src/md/Makefile @@ -0,0 +1,57 @@ +# +# 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_ARCH),MAC) +DIRS = mac +else + ifeq ($(OS_ARCH),WINNT) + ifeq ($(OS_TARGET),OS2) + DIRS = os2 + else + DIRS = windows + endif + else + DIRS = unix + endif +endif + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + prosdep.c \ + $(NULL) + +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/mac/MacErrorHandling.h b/pr/src/md/mac/MacErrorHandling.h new file mode 100644 index 00000000..8b9b795f --- /dev/null +++ b/pr/src/md/mac/MacErrorHandling.h @@ -0,0 +1,633 @@ + +/********************************************************************* + +FILENAME + Exceptions.h + +DESCRIPTION + A collection of routines and macros to handle assertions and + exceptions. + +COPYRIGHT + Copyright © Apple Computer, Inc. 1989-1991 + All rights reserved. + +ROUTINES + EXTERNALS + dprintf + check_dprintf + checkpos_dprintf + +MACROS + EXTERNALS + check + ncheck + check_action + ncheck_action + require + nrequire + require_action + nrequire_action + resume + +MODIFICATION HISTORY + Nov 12 95 BKJ Moved to MetroWerks environment & the NSPR + +NOTE + To keep code size down, use these routines and macros with the C + compiler option -b2 or -b3. This will eliminate duplicate strings + within a procedure. + +*********************************************************************/ + +#ifndef __MACERRORHANDLING__ +#define __MACERRORHANDLING__ + +/********************************************************************* + +INCLUDES + +*********************************************************************/ + +#include <Types.h> + +/*<FF>*/ +/********************************************************************* + +CONSTANTS AND CONTROL + +*********************************************************************/ + +/* + These defines are used to control the amount of information + displayed when an assertion fails. DEBUGOFF and WARN will run + silently. MIN will simply break into the debugger. ON will break + and display the assertion that failed and the exception (for + require statements). FULL will also display the source file name + and line number. SYM does a SysBreak and is usefull when using a + symbolic debugger like SourceBug or SADE. They should be set into + DEBUGLEVEL. The default LEVEL is OFF. +*/ + +#define DEBUGOFF 0 +#define DEBUGWARN 1 +#define DEBUGMIN 2 +#define DEBUGON 3 +#define DEBUGFULL 4 +#define DEBUGSYM 6 + +#ifndef DEBUGLEVEL +#define DEBUGLEVEL DEBUGOFF +#endif DEBUGLEVEL + +/* + resumeLabel is used to control the insertion of labels for use with + the resume macro. If you do not use the resume macro and you wish + to have multible exceptions per label then you can add the + following define to you source code. + +*/ +#define resumeLabel(exception) // Multiple exceptions per label +// #define resumeLabel(exception) resume_ ## exception: // Single exception per label + + +/* + traceon and debugon are used to test for options +*/ + +#define traceon ((DEBUGLEVEL > DEBUGWARN) && defined(TRACEON)) +#define debugon (DEBUGLEVEL > DEBUGWARN) + +/* + Add some macros for DEBUGMIN and DEBUGSYM to keep the size down. +*/ + +#define __DEBUGSMALL ((DEBUGLEVEL == DEBUGMIN) || \ + (DEBUGLEVEL == DEBUGSYM)) + +#if DEBUGLEVEL == DEBUGMIN +#define __DebuggerBreak Debugger() +#elif DEBUGLEVEL == DEBUGSYM +#define __DebuggerBreak SysBreak() +#endif + + +/*<FF>*/ +/********************************************************************* + +MACRO + check(assertion) + +DESCRIPTION + If debugging is on then check will test assertion and if it fails + break into the debugger. Otherwise check does nothing. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define check(assertion) \ + do { \ + if (assertion) ; \ + else __DebuggerBreak; \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define check(assertion) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define check(assertion) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __FILE__, __LINE__); \ + } \ + } while (false) + +#else + +#define check(assertion) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + ncheck(assertion) + +DESCRIPTION + If debugging is on then ncheck will test !assertion and if it fails + break into the debugger. Otherwise ncheck does nothing. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define ncheck(assertion) \ + do { \ + if (assertion) __DebuggerBreak; \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define ncheck(assertion) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed", \ + #assertion, __privateAssertion); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define ncheck(assertion) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __privateAssertion, __FILE__, __LINE__); \ + } \ + } while (false) + +#else + +#define ncheck(assertion) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + check_action(assertion, action) + +DESCRIPTION + If debugging is on then check_action will test assertion and if it + fails break into the debugger then execute action. Otherwise + check_action does nothing. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define check_action(assertion, action) \ + do { \ + if (assertion) ; \ + else { \ + __DebuggerBreak; \ + { action } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define check_action(assertion, action) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed", #assertion); \ + { action } \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define check_action(assertion, action) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __FILE__, __LINE__); \ + { action } \ + } \ + } while (false) + +#else + +#define check_action(assertion, action) + +#endif + +/*<FF>*/ +/************************************************************************************** + +MACRO + ncheck_action(assertion, action) + +DESCRIPTION + If debugging is on then ncheck_action will test !assertion and if + it fails break into the debugger then execute action. Otherwise + ncheck_action does nothing. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define ncheck_action(assertion, action) \ + do { \ + if (assertion) { \ + __DebuggerBreak; \ + { action } \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define ncheck_action(assertion, action) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed", \ + #assertion, __privateAssertion); \ + { action } \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define ncheck_action(assertion, action) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __privateAssertion, __FILE__, __LINE__); \ + { action } \ + } \ + } while (false) + +#else + +#define ncheck_action(assertion, action) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + require(assertion, exception) + +DESCRIPTION + require will test assertion and if it fails: + break into the debugger if debugging is on. + goto exception. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define require(assertion, exception) \ + do { \ + if (assertion) ; \ + else { \ + __DebuggerBreak; \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define require(assertion, exception) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "Exception \"%s\" Raised", \ + #assertion, #exception); \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define require(assertion, exception) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "Exception \"%s\" Raised\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, #exception, __FILE__, __LINE__); \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#else + +#define require(assertion, exception) \ + do { \ + if (assertion) ; \ + else { \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + nrequire(assertion, exception) + +DESCRIPTION + nrequire will test !assertion and if it fails: + break into the debugger if debugging is on. + goto exception. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define nrequire(assertion, exception) \ + do { \ + if (assertion) { \ + DebugStr(); \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define nrequire(assertion, exception) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "Exception \"%s\" Raised", \ + #assertion, __privateAssertion, #exception); \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define nrequire(assertion, exception) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "Exception \"%s\" Raised\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __privateAssertion, #exception, __FILE__, \ + __LINE__); \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#else + +#define nrequire(assertion, exception) \ + do { \ + if (assertion) { \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + require_action(assertion, exception, action) + +DESCRIPTION + require_action will test assertion and if it fails: + break into the debugger if debugging is on. + execute action. + goto exception. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define require_action(assertion, exception, action) \ + do { \ + if (assertion) ; \ + else { \ + __DebuggerBreak; \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define require_action(assertion, exception, action) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "Exception \"%s\" Raised", \ + #assertion, #exception); \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define require_action(assertion, exception, action) \ + do { \ + if (assertion) ; \ + else { \ + dprintf(notrace, "Assertion \"%s\" Failed\n" \ + "Exception \"%s\" Raised\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, #exception, __FILE__, __LINE__); \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#else + +#define require_action(assertion, exception, action) \ + do { \ + if (assertion) ; \ + else { \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + nrequire_action(assertion, exception, action) + +DESCRIPTION + nrequire_action will test !assertion and if it fails: + break into the debugger if debugging is on. + execute action. + goto exception. + +*********************************************************************/ + +#if __DEBUGSMALL + +#define nrequire_action(assertion, exception, action) \ + do { \ + if (assertion) { \ + __DebuggerBreak; \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGON + +#define nrequire_action(assertion, exception, action) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "Exception \"%s\" Raised", \ + #assertion, __privateAssertion, #exception); \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#elif DEBUGLEVEL == DEBUGFULL + +#define nrequire_action(assertion, exception, action) \ + do { \ + void* __privateAssertion = (void*)(assertion); \ + \ + if (__privateAssertion) { \ + dprintf(notrace, "Assertion \"!(%s [= %#08X])\" Failed\n" \ + "Exception \"%s\" Raised\n" \ + "File: %s\n" \ + "Line: %d", \ + #assertion, __privateAssertion, #exception, __FILE__, \ + __LINE__); \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#else + +#define nrequire_action(assertion, exception, action) \ + do { \ + if (assertion) { \ + { action } \ + goto exception; \ + resumeLabel(exception); \ + } \ + } while (false) + +#endif + +/*<FF>*/ +/********************************************************************* + +MACRO + resume(exception) + +DESCRIPTION + resume will resume execution after the n/require/_action statement + specified by exception. Resume lables must be on (the default) in + order to use resume. If an action form of require was used then the + action will not be re-executed. + +*********************************************************************/ + + +#define resume(exception) \ + do { \ + goto resume_ ## exception; \ + } while (false) + + +/*<FF>*/ +/********************************************************************/ +#endif diff --git a/pr/src/md/mac/macdll.c b/pr/src/md/mac/macdll.c new file mode 100644 index 00000000..1a06d59e --- /dev/null +++ b/pr/src/md/mac/macdll.c @@ -0,0 +1,361 @@ +/* -*- 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. + */ + +#undef OLDROUTINENAMES +#define OLDROUTINENAMES 1 + +#include <Files.h> +#include <Errors.h> +#include <Folders.h> +#include <CodeFragments.h> +#include <Aliases.h> + +#include "MacErrorHandling.h" +#include "primpl.h" +#include "plstr.h" + +typedef struct CfrgItem CfrgItem, *CfrgItemPtr; +struct CfrgItem +{ + OSType fArchType; + UInt32 fUpdateLevel; + UInt32 fCurVersion; + UInt32 fOldDefVersion; + UInt32 fAppStackSize; + UInt16 fAppSubFolder; + UInt8 fUsage; + UInt8 fLocation; + UInt32 fCodeOffset; + UInt32 fCodeLength; + UInt32 fReserved1; + UInt32 fReserved2; + UInt16 fItemSize; // %4 == 0 + Str255 fName; + // Only make the final p-string as long as needed, then align to + // a longword boundary +}; + +typedef struct CfrgHeader CfrgHeader, *CfrgHeaderPtr, **CfrgHeaderHandle; +struct CfrgHeader +{ + UInt32 fReserved1; + UInt32 fReserved2; + UInt32 fVersion; + UInt32 fReserved3; + UInt32 fReserved4; + UInt32 fFiller1; + UInt32 fFiller2; + UInt32 fItemCount; + CfrgItem fCfrgItemArray[1]; +}; + +/* + turds used to iterate through the directories looking + for the desired library. +*/ + +struct GetSharedLibraryFilterProcData +{ + Boolean inRecursive; + StringPtr inName; + + Boolean outFound; + CFragConnectionID outID; + Ptr outAddress; + OSErr outError; +}; +typedef struct GetSharedLibraryFilterProcData GetSharedLibraryFilterProcData; + +static pascal void +GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData); + + +/* + NSGetSharedLibrary + + Unfortunately CFM doesn't support user specified loader paths, + so we emulate the behavior. Effectively this is a GetSharedLibrary + where the loader path is user defined. +*/ + +extern OSErr +NSGetSharedLibrary(Str255 inLibName, CFragConnectionID* outID, Ptr* outMainAddr) +{ + char* curLibPath; + char* freeCurLibPath; + OSErr tempErr; + Boolean recursive; + FSSpec curFolder; + GetSharedLibraryFilterProcData filterData; + char *endCurLibPath; + Boolean done; + + filterData.outFound = false; + filterData.outID = (CFragConnectionID)(-1); + filterData.outAddress = NULL; + filterData.inName = inLibName; + + freeCurLibPath = curLibPath = strdup(PR_GetLibraryPath()); + + if (curLibPath == NULL) + return (fragLibNotFound); + + tempErr = fragLibNotFound; + + do + { + endCurLibPath = PL_strchr(curLibPath, PR_PATH_SEPARATOR); + done = (endCurLibPath == NULL); + +#if 0 + // we overload the first character of a path if it's : + // then we want to recursively search that path + // see if path should be recursive + if (*curLibPath == ':') + { + // ':' is an illegal character in the name of a file + // if we start any path with this, we want to allow + // search recursively + curLibPath++; + recursive = true; + } + else +#endif + { + recursive = false; + } + + if (!done) + *endCurLibPath = '\0'; // NULL terminate the string + + // convert to FSSpec + tempErr = ConvertUnixPathToFSSpec(curLibPath, &curFolder); + + // now look in this directory + if (noErr == tempErr) + { + filterData.inRecursive = recursive; + FSpIterateDirectory(&curFolder, recursive ? 0 : 1, &GetSharedLibraryFilterProc, &filterData); + + if (filterData.outFound) + { + *outID = filterData.outID; + *outMainAddr = filterData.outAddress; + tempErr = noErr; + break; + } + else + { + tempErr = fragLibNotFound; + } + } + + curLibPath = endCurLibPath + 1; // skip to next path (past the '\0'); + } while (!done); + + free(freeCurLibPath); + return (tempErr); +} + + +static Boolean +LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength); + + +/* + GetSharedLibraryFilterProc + + Callback to FSpIterateDirectory, finds a library with the name matching the + data in inFilterData (of type GetSharedLibraryFilterProcData). Forces a quit + when a match is found. +*/ + +static pascal void +GetSharedLibraryFilterProc(const CInfoPBRec* const inCpb, Boolean* inWantQuit, void *inFilterData) +{ + GetSharedLibraryFilterProcData* pFilterData = (GetSharedLibraryFilterProcData*) inFilterData; + + if ((inCpb->hFileInfo.ioFlAttrib & (1 << ioDirFlg)) == 0) + { + FSSpec fragSpec; + OSErr tempErr; + Str255 errName; + Boolean crap; + UInt32 codeOffset; + UInt32 codeLength; + + // it's a file + + // ¥ fix-me do we really want to allow all 'APPL's' for in which to find this library? + switch (inCpb->hFileInfo.ioFlFndrInfo.fdType) + { + case kCFragLibraryFileType: + case 'APPL': + tempErr = FSMakeFSSpec(inCpb->hFileInfo.ioVRefNum, inCpb->hFileInfo.ioFlParID, inCpb->hFileInfo.ioNamePtr, &fragSpec); + + // this shouldn't fail + if (noErr != tempErr) + { + return; + } + + // resolve an alias if this was one + tempErr = ResolveAliasFile(&fragSpec, true, &crap, &crap); + + // if got here we have a shlb (or app-like shlb) + if (noErr != tempErr) + { + // probably couldn't resolve an alias + return; + } + + break; + default: + return; + } + + // see if this symbol is in this fragment + if (LibInPefContainer(&fragSpec, pFilterData->inName, &codeOffset, &codeLength)) + tempErr = GetDiskFragment(&fragSpec, codeOffset, codeLength, pFilterData->inName, kLoadLib, &pFilterData->outID, &pFilterData->outAddress, errName); + else + return; + + // stop if we found a library by that name + if (noErr == tempErr) + { + *inWantQuit = true; + pFilterData->outFound = true; + pFilterData->outError = tempErr; + } + } + // FSpIterateDirectory will automagically call us for subsequent sub-dirs if necessary +} + + +/* + LibInPefContainer + + Tell whether library inName is contained it the file pointed to by inSpec. + Return the codeOffset and codeLength information, for a subsequent + call to GetDiskFragment. +*/ + +static Boolean +LibInPefContainer(const FSSpec* inSpec, StringPtr inName, UInt32* outCodeOffset, UInt32* outCodeLength) +{ + short refNum; + CfrgHeaderHandle hCfrg; + CfrgItem* pCurItem; + UInt32 curLibIndex; + Boolean found; + + // asume we didn't find it + found = false; + + // open the resource fork, if we can't bail + refNum = FSpOpenResFile(inSpec, fsRdPerm); + require(-1 != refNum, Exit); + + // grab out the alias record, if it's not there bail + hCfrg = (CfrgHeaderHandle) Get1Resource(kCFragResourceType, kCFragResourceID); + require(NULL != hCfrg, CloseResourceAndExit); + + HLock((Handle)hCfrg); + + // get ptr to first item + pCurItem = &(*hCfrg)->fCfrgItemArray[0]; + for (curLibIndex = 0; curLibIndex < (*hCfrg)->fItemCount; curLibIndex++) + { + // is this our library? + if ((pCurItem->fName[0] == inName[0]) && + (strncmp((char*) inName + 1, (char*) pCurItem->fName + 1, PR_MIN(pCurItem->fName[0], inName[0])) == 0)) + { + *outCodeOffset = pCurItem->fCodeOffset; + *outCodeLength = pCurItem->fCodeLength; + found = true; + } + + // skip to next one + pCurItem = (CfrgItem*) ((char*) pCurItem + pCurItem->fItemSize); + } + + HUnlock((Handle)hCfrg); + +CloseResourceAndExit: + CloseResFile(refNum); +Exit: + return (found); + +} + + +/* + NSFindSymbol + + Workaround bug in CFM FindSymbol (in at least 7.5.5) where symbols with lengths + greater than 63 chars cause a "paramErr". We iterate through all symbols + in the library to find the desired symbol. +*/ + +extern OSErr +NSFindSymbol(CFragConnectionID inID, Str255 inSymName, Ptr* outMainAddr, SymClass *outSymClass) +{ + OSErr err; + + if (inSymName[0] > 63) + { + /* + if there are greater than 63 characters in the + name, CFM FindSymbol fails, so let's iterate through all + of the symbols in the fragment and grab it + that way. + */ + long symbolCount; + Str255 curSymName; + long curIndex; + Boolean found; + + found = false; + err = CountSymbols(inID, &symbolCount); + if (noErr == err) + { + /* now iterate through all the symbols in the library */ + /* per DTS the indices apparently go 0 to n-1 */ + for (curIndex = 0; (curIndex <= symbolCount - 1 && !found); curIndex++) + { + err = GetIndSymbol(inID, curIndex, curSymName, outMainAddr, outSymClass); + if (noErr == err && curSymName[0] == inSymName[0] && !strncmp((char*)curSymName + 1, (char*)inSymName + 1, curSymName[0])) + { + /* found our symbol */ + found = true; + } + } + + /* if we didn't find it set the error code so below it won't take this symbol */ + if (!found) + err = fragSymbolNotFound; + } + } + else + { + err = FindSymbol(inID, inSymName, outMainAddr, outSymClass); + } + + return (err); +} + diff --git a/pr/src/md/mac/macio.c b/pr/src/md/mac/macio.c new file mode 100644 index 00000000..050d2529 --- /dev/null +++ b/pr/src/md/mac/macio.c @@ -0,0 +1,1897 @@ +/* -*- 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 <Types.h> +#include <Files.h> +#include <Folders.h> +#include <Errors.h> + +#include <fcntl.h> + +#include "primpl.h" +#include "MacErrorHandling.h" + +/* forward declarations */ +OSErr ConvertUnixPathToMacPath(const char *, char **); +OSErr ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec); +extern unsigned long gJanuaryFirst1970Seconds; + +extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout); +extern void DoneWaitingOnThisThread(PRThread *thread); + +/* PB for Read and Write */ +struct ExtendedParamBlock { + /* PB must be first so that the file system can get the right data. */ + ParamBlockRec pb; + PRThread *thread; +}; +typedef struct ExtendedParamBlock ExtendedParamBlock; + + +/* XXX Not done yet for 68K */ +/* I/O completion routne for _MD_READ and _MD_WRITE */ +static void AsyncIOCompletion (ExtendedParamBlock *pbAsyncPtr) +{ + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + PRThread *thread = pbAsyncPtr->thread; + + if (_PR_MD_GET_INTSOFF()) { + cpu->u.missed[cpu->where] |= _PR_MISSED_IO; + thread->md.notifyPending = PR_TRUE; + return; + } + _PR_SET_INTSOFF(1); + + thread->md.osErrCode = noErr; + DoneWaitingOnThisThread(thread); + + _PR_SET_INTSOFF(0); + +} + +void _MD_SetError(OSErr oserror) +{ + PRErrorCode code; + + switch (oserror) { + case memFullErr: + code = PR_OUT_OF_MEMORY_ERROR; + break; + case fnfErr: + code = PR_FILE_NOT_FOUND_ERROR; + break; + case dupFNErr: + code = PR_FILE_EXISTS_ERROR; + break; + case ioErr: + code = PR_IO_ERROR; + break; + case nsvErr: + case wrgVolTypErr: + code = PR_INVALID_DEVICE_STATE_ERROR; + break; + case bdNamErr: + case fsRnErr: + code = PR_NAME_TOO_LONG_ERROR; + break; + case tmfoErr: + code = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case opWrErr: + case wrPermErr: + case permErr: + case afpAccessDenied: + code = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case afpObjectTypeErr: + code = PR_DIRECTORY_LOOKUP_ERROR; + break; + case wPrErr: + case vLckdErr: + code = PR_DEVICE_IS_LOCKED_ERROR; + break; + case fLckdErr: + code = PR_FILE_IS_LOCKED_ERROR; + break; + case dirNFErr: + code = PR_NOT_DIRECTORY_ERROR; + break; + case dirFulErr: + code = PR_MAX_DIRECTORY_ENTRIES_ERROR; + break; + case dskFulErr: + code = PR_NO_DEVICE_SPACE_ERROR; + break; + case rfNumErr: + case fnOpnErr: + code = PR_BAD_DESCRIPTOR_ERROR; + break; + case eofErr: + code = PR_END_OF_FILE_ERROR; + break; + case posErr: + case gfpErr: + code = PR_FILE_SEEK_ERROR; + break; + case fBsyErr: + code = PR_FILE_IS_BUSY_ERROR; + break; + case extFSErr: + code = PR_REMOTE_FILE_ERROR; + break; + case abortErr: + code = PR_PENDING_INTERRUPT_ERROR; + break; + case paramErr: + code = PR_INVALID_ARGUMENT_ERROR; + break; + case unimpErr: + code = PR_NOT_IMPLEMENTED_ERROR; + break; + } + + PR_SetError(code, oserror); +} + +void _MD_IOInterrupt(void) +{ + PRCList *qp; + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + + _PR_SLEEPQ_LOCK(me->cpu); + qp = _PR_PAUSEQ(me->cpu).next; + while (qp != &_PR_PAUSEQ(me->cpu)) { + + thread = _PR_THREAD_PTR(qp); + PR_ASSERT(thread->flags & _PR_ON_PAUSEQ); + + qp = qp->next; + + if (thread->md.notifyPending) { + thread->md.notifyPending = PR_FALSE; + DoneWaitingOnThisThread(thread); + } + } + qp = _PR_SLEEPQ(me->cpu).next; + while (qp != &_PR_SLEEPQ(me->cpu)) { + + thread = _PR_THREAD_PTR(qp); + PR_ASSERT(thread->flags & _PR_ON_SLEEPQ); + + qp = qp->next; + + if (thread->md.notifyPending) { + thread->md.notifyPending = PR_FALSE; + DoneWaitingOnThisThread(thread); + } + } + _PR_SLEEPQ_UNLOCK(thread->cpu); +} + +/* +** All PR_read and PR_Write calls are synchronous from caller's perspective. +** They are internally made asynchronous calls. This gives cpu to other +** user threads while the async io is in progress. +*/ +PRInt32 ReadWriteProc(PRFileDesc *fd, void *buf, PRUint32 bytes, IOOperation op) +{ + PRInt32 refNum = fd->secret->md.osfd; + OSErr err; + ExtendedParamBlock pbAsync; + PRThread *me = _PR_MD_CURRENT_THREAD(); + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); +#if 0 + /* quick hack to allow PR_fprintf, etc to work with stderr, stdin, stdout */ + /* note, if a user chooses "seek" or the like as an operation in another function */ + /* this will not work */ + FILE* systemFP = NULL; + extern int errno; + + switch (refNum) + { + case 0: + systemFP = stdin; + break; + case 1: + systemFP = stdout; + break; + case 2: + systemFP = stderr; + break; + } + + if (systemFP) + { + size_t bytesDone; + + if (op == READ_ASYNC) + bytesDone = fread(buf, 1, bytes, systemFP); + else + bytesDone = fwrite(buf, 1, bytes, systemFP); + + if (errno) + { + err = errno; + goto ErrorExit; + } + else + return (bytesDone); + } + else +#else + if (refNum >= 0 && refNum < 3) + { + PR_ASSERT(FALSE); /* writing to these is hazardous to a Mac's health (refNum 2 is the system file) */ + err = paramErr; + goto ErrorExit; + } +#endif + { + /* grab the thread so we know which one to post to at completion */ + pbAsync.thread = me; + + pbAsync.pb.ioParam.ioCompletion = NewIOCompletionProc((ProcPtr)&AsyncIOCompletion); + pbAsync.pb.ioParam.ioResult = noErr; + pbAsync.pb.ioParam.ioRefNum = refNum; + pbAsync.pb.ioParam.ioBuffer = buf; + pbAsync.pb.ioParam.ioReqCount = bytes; + pbAsync.pb.ioParam.ioPosMode = fsAtMark; + pbAsync.pb.ioParam.ioPosOffset = 0; + + /* + ** Issue the async read call and wait for the io semaphore associated + ** with this thread. + ** Don't compute error code from async call. Bug in OS returns a garbage value. + */ + me->io_pending = PR_TRUE; + me->io_fd = refNum; + me->md.osErrCode = noErr; + if (op == READ_ASYNC) + (void) PBReadAsync(&pbAsync); + else + (void) PBWriteAsync(&pbAsync); + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + } + + err = me->md.osErrCode; + if (err != noErr) + goto ErrorExit; + + err = pbAsync.pb.ioParam.ioResult; + if (err != noErr && err != eofErr) + goto ErrorExit; + else + return pbAsync.pb.ioParam.ioActCount; + +ErrorExit: + me->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +/* +Special WriteSyncProc for logging only. IO occurs synchronously. Otherwise, +logging internal to NSPR causes ReadWriteProc above to recurse on PR_WaitSem logging. +*/ +PRInt32 WriteSyncProc(PRFileDesc *fd, void *buf, PRUint32 bytes) +{ + PRInt32 refNum = fd->secret->md.osfd; + OSErr err; + ParamBlockRec pb; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (refNum >= 0 && refNum < 3) + { + PR_ASSERT(FALSE); /* writing to these is hazardous to a Mac's health (refNum 2 is the system file) */ + err = paramErr; + goto ErrorExit; + } + + pb.ioParam.ioCompletion = NULL; + pb.ioParam.ioResult = noErr; + pb.ioParam.ioRefNum = refNum; + pb.ioParam.ioBuffer = buf; + pb.ioParam.ioReqCount = bytes; + pb.ioParam.ioPosMode = fsAtMark; + pb.ioParam.ioPosOffset = 0; + + err = PBWriteSync(&pb); + + if (err != noErr) + goto ErrorExit; + else + return pb.ioParam.ioActCount; + +ErrorExit: + me->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +/* File I/O functions called by PR I/O routines */ +PRInt32 _MD_Open(const char *path, PRIntn oflag, int mode) +{ +// Macintosh doesn¹t really have mode bits, just drop them +#pragma unused (mode) + + OSErr err; + ParamBlockRec pb; + char *macFileName = NULL; + Str255 pascalName; + PRInt8 perm; + PRInt32 flags = oflag; + + if (flags & PR_RDWR) { + oflag = O_RDWR; + } else if (flags & PR_WRONLY) { + oflag = O_WRONLY; + } else { + oflag = O_RDONLY; + } + + if (flags & PR_CREATE_FILE) { + oflag |= O_CREAT ; + } + + err = ConvertUnixPathToMacPath(path, &macFileName); + + if (err != noErr) + goto ErrorExit; + + pb.ioParam.ioCompletion = NULL; + PStrFromCStr(macFileName, pascalName); + PR_DELETE(macFileName); + pb.ioParam.ioNamePtr = pascalName; + pb.ioParam.ioVRefNum = 0; + pb.ioParam.ioVersNum = 0; + +open: + perm = oflag & 3; + if (perm == O_RDWR) + perm = fsRdWrPerm; + else if (perm == O_WRONLY) + perm = fsWrPerm; + else + perm = fsRdPerm; + pb.ioParam.ioPermssn = perm; + + pb.ioParam.ioMisc = NULL; + + err = PBOpenSync(&pb); + if (err == noErr) + return pb.ioParam.ioRefNum; + else if ((err != fnfErr) || ((oflag & O_CREAT) == 0)) + goto ErrorExit; + + err = PBCreateSync(&pb); + if (err == noErr) + goto open; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +/* _MD_CLOSE_FILE, _MD_READ, _MD_WRITE, _MD_GET_FILE_ERROR are defined in _macos.h */ + +PRInt32 _MD_LSeek(PRFileDesc *fd, PRInt32 offset, int how) +{ + PRInt32 refNum = fd->secret->md.osfd; + OSErr err = noErr; + long curPos, endPos; + + PR_ASSERT(offset >= 0); + + /* compute new mark */ + switch (how) { + case PR_SEEK_SET: + endPos = offset; + break; + + case PR_SEEK_CUR: + err = GetFPos(refNum, &curPos); + endPos = curPos + offset; + break; + + case PR_SEEK_END: + err = GetEOF(refNum, &curPos); + endPos = curPos + offset; + break; + + default: + err = paramErr; + break; + } + + /* set the new mark and extend the file if seeking beyond current EOF */ + if (err == noErr) { + err = SetFPos(refNum, fsFromStart, endPos); + if (err == eofErr) { + err = SetEOF(refNum, endPos); + } + } + + if (err == noErr) { + return endPos; + } else { + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; + } +} + +PRInt32 _MD_FSync(PRFileDesc *fd) +{ + PRInt32 refNum = fd->secret->md.osfd; + OSErr err; + ParamBlockRec pb; + + pb.ioParam.ioCompletion = NULL; + pb.ioParam.ioRefNum = refNum; + + err = PBFlushFileSync(&pb); + if (err != noErr) + goto ErrorExit; + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +#include "plstr.h" + +PRStatus _MD_OpenDir(_MDDir *mdDir,const char *name) +{ + // Emulate the Unix opendir() routine. + + OSErr err; + CInfoPBRec pb; + char *macDirName = NULL; + char *position = NULL; + char volumeName[32]; + Str255 pascalName; + + // Get the Macintosh path + err = ConvertUnixPathToMacPath(name, &macDirName); + if (err != noErr) + goto ErrorExit; + + // Get the vRefNum + position = PL_strchr(macDirName, PR_PATH_SEPARATOR); + if ((position == macDirName) || (position == NULL)) + mdDir->ioVRefNum = 0; // Use application relative searching + else { + memset(volumeName, 0, sizeof(volumeName)); + strncpy(volumeName, macDirName, position-macDirName); + mdDir->ioVRefNum = GetVolumeRefNumFromName(volumeName); + } + + // Get info about the object. + PStrFromCStr(macDirName, pascalName); + PR_DELETE(macDirName); + + pb.dirInfo.ioNamePtr = pascalName; + pb.dirInfo.ioVRefNum = mdDir->ioVRefNum; + pb.dirInfo.ioDrDirID = 0; + pb.dirInfo.ioFDirIndex = 0; + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + + // Are we dealing with a directory? + if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) { + err = dirNFErr; + goto ErrorExit; + } + + /* This is a directory, store away the pertinent information. + ** We post increment. I.e. index is always the nth. item we + ** should get on the next call + */ + mdDir->ioDirID = pb.dirInfo.ioDrDirID; + mdDir->currentEntryName = NULL; + mdDir->ioFDirIndex = 1; + return PR_SUCCESS; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return PR_FAILURE; +} + +char *_MD_ReadDir(_MDDir *mdDir, PRIntn flags) +{ + // Emulate the Unix readdir() routine. + + // Mac doesnÕt have the concept of .(PR_SKIP_DOT) & ..(PR_SKIP_DOT_DOT) + + OSErr err; + CInfoPBRec pb; + char *returnedCStr; + Str255 pascalName = "\p"; + PRBool foundEntry; + + PR_ASSERT(mdDir != NULL); + + do { + + // Release the last name read. + PR_DELETE(mdDir->currentEntryName); + mdDir->currentEntryName = NULL; + + // WeÕve got all the info we need, just get info about this guy. + pb.hFileInfo.ioNamePtr = pascalName; + pb.hFileInfo.ioVRefNum = mdDir->ioVRefNum; + pb.hFileInfo.ioFDirIndex = mdDir->ioFDirIndex; + pb.hFileInfo.ioDirID = mdDir->ioDirID; + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + + // Convert the Pascal string to a C string (actual allocation occurs in CStrFromPStr) + CStrFromPStr(pascalName, &returnedCStr); + + mdDir->currentEntryName = returnedCStr; + mdDir->ioFDirIndex++; + + // If it is not a hidden file and the flags did not specify skipping, we are done. + if ((flags & PR_SKIP_HIDDEN) && (pb.hFileInfo.ioFlFndrInfo.fdFlags & fInvisible)) + foundEntry = PR_FALSE; + else + foundEntry = PR_TRUE; + + } while (!foundEntry); + + return (mdDir->currentEntryName); + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return NULL; +} + + +void _MD_CloseDir(_MDDir *mdDir) +{ + // Emulate the Unix closedir() routine + + PR_DELETE(mdDir->currentEntryName); +} + +PRInt32 _MD_MkDir(char *unixPath, PRIntn mode) +{ + HFileParam fpb; + Str255 pascalName = "\p"; + char *cMacPath = NULL; + OSErr err; + + #pragma unused (mode) // Mode is ignored on the Mac + + if (unixPath) { + err = ConvertUnixPathToMacPath(unixPath, &cMacPath); + if (err != noErr) + goto ErrorExit; + + PStrFromCStr(cMacPath, pascalName); + PR_DELETE(cMacPath); + fpb.ioNamePtr = pascalName; + fpb.ioVRefNum = 0; + fpb.ioDirID = 0L; + + err = PBDirCreateSync((HParmBlkPtr)&fpb); + if (err != noErr) + goto ErrorExit; + } + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRInt32 _MD_Delete(char *unixPath) +{ + HFileParam fpb; + Str255 pascalName = "\p"; + char *cMacPath = NULL; + OSErr err; + + if (unixPath) { + err = ConvertUnixPathToMacPath(unixPath, &cMacPath); + if (err != noErr) + goto ErrorExit; + + PStrFromCStr(cMacPath, pascalName); + PR_DELETE(cMacPath); + fpb.ioNamePtr = pascalName; + fpb.ioVRefNum = 0; + fpb.ioDirID = 0L; + + err = PBHDeleteSync((HParmBlkPtr)&fpb); + if (err != noErr) + goto ErrorExit; + } + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRInt32 _MD_Rename(char *fromUnixPath, char *toUnixPath) +{ + OSErr err; + FSSpec fromSpec; + FSSpec toSpec; + FSSpec destDirSpec; + FSSpec beforeRenameSpec; + + if (fromUnixPath && toUnixPath) { + err = ConvertUnixPathToFSSpec(fromUnixPath, &fromSpec); + if (err != noErr) + goto ErrorExit; + + err = ConvertUnixPathToFSSpec(toUnixPath, &toSpec); + if (err != noErr && err != fnfErr) + goto ErrorExit; + + /* make an FSSpec for the destination directory */ + err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, nil, &destDirSpec); + if (err != noErr) /* parent directory must exist */ + goto ErrorExit; + + // move it to the directory specified + err = FSpCatMove(&fromSpec, &destDirSpec); + if (err != noErr) + goto ErrorExit; + + // make a new FSSpec for the file or directory in its new location + err = FSMakeFSSpec(toSpec.vRefNum, toSpec.parID, fromSpec.name, &beforeRenameSpec); + if (err != noErr) + goto ErrorExit; + + // rename the file or directory + err = FSpRename(&beforeRenameSpec, toSpec.name); + if (err != noErr) + goto ErrorExit; + + } else { + err = paramErr; + goto ErrorExit; + } + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +#define kWriteAccessAllowed (0x100) +PRInt32 _MD_Access(char *unixPath, int amode) +{ + // + // Emulate the Unix access routine + // + + OSErr err; + CInfoPBRec pb; + FCBPBRec fcbpb; + char *cMacPath = NULL; + Str255 pascalMacPath; + struct stat info; + + // Convert to a Mac style path + err = ConvertUnixPathToMacPath(unixPath, &cMacPath); + if (err != noErr) + goto ErrorExit; + + err = stat(cMacPath, &info); + if (err != noErr) + goto ErrorExit; + + + // If all weÕre doing is checking for the existence of the file, weÕre out of here. + // On the Mac, if a file exists, you can read from it. + // This doesnÕt handle remote AppleShare volumes. Does it need to? + if ((amode == PR_ACCESS_EXISTS) || (amode == PR_ACCESS_READ_OK)) { + goto success; + } + + PStrFromCStr(cMacPath, pascalMacPath); + + pb.hFileInfo.ioNamePtr = pascalMacPath; + pb.hFileInfo.ioVRefNum = info.st_dev; + pb.hFileInfo.ioDirID = 0; + pb.hFileInfo.ioFDirIndex = 0; + + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + // Check out all the access permissions. + + if (amode == PR_ACCESS_WRITE_OK) { + fcbpb.ioNamePtr = NULL; + fcbpb.ioVRefNum = pb.hFileInfo.ioVRefNum; + fcbpb.ioRefNum = pb.hFileInfo.ioFRefNum; + fcbpb.ioFCBIndx = 0; + + err = PBGetFCBInfoSync(&fcbpb); + if (err != noErr) + goto ErrorExit; + + /* Look at Inside Mac IV-180 */ + if ((fcbpb.ioFCBFlags & kWriteAccessAllowed) == 0) { + err = permErr; + goto ErrorExit; + } + } + +success: + PR_DELETE(cMacPath); + return 0; + +ErrorExit: + if (cMacPath != NULL) + PR_DELETE(cMacPath); + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRInt32 _MD_GetFileInfo(char *unixPath, PRFileInfo *info) +{ + CInfoPBRec pb; + OSErr err; + char *cMacPath = NULL; + Str255 pascalMacPath; + PRTime oneMillion, dateInMicroSeconds; + + // Convert to a Mac style path + err = ConvertUnixPathToMacPath(unixPath, &cMacPath); + if (err != noErr) + goto ErrorExit; + + PStrFromCStr(cMacPath, pascalMacPath); + PR_DELETE(cMacPath); + + pb.hFileInfo.ioNamePtr = pascalMacPath; + pb.hFileInfo.ioVRefNum = 0; + pb.hFileInfo.ioDirID = 0; + pb.hFileInfo.ioFDirIndex = 0; + + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + + if (pb.hFileInfo.ioFlAttrib & ioDirMask) { + info->type = PR_FILE_DIRECTORY; + info->size = 0; + } else { + info->type = PR_FILE_FILE; + info->size = pb.hFileInfo.ioFlLgLen + pb.hFileInfo.ioFlRLgLen; + } + + pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds; + LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat); + LL_I2L(oneMillion, PR_USEC_PER_SEC); + LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds); + + pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds; + LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat); + LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds); + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRInt32 _MD_GetOpenFileInfo(const PRFileDesc *fd, PRFileInfo *info) +{ + OSErr err; + FCBPBRec fcbpb; + CInfoPBRec pb; + Str255 pascalMacPath; + PRTime oneMillion, dateInMicroSeconds; + + fcbpb.ioNamePtr = pascalMacPath; + fcbpb.ioVRefNum = 0; + fcbpb.ioRefNum = fd->secret->md.osfd; + fcbpb.ioFCBIndx = 0; + + err = PBGetFCBInfoSync(&fcbpb); + if (err != noErr) + goto ErrorExit; + + info->type = PR_FILE_FILE; + info->size = fcbpb.ioFCBEOF; + + pb.hFileInfo.ioNamePtr = pascalMacPath; + pb.hFileInfo.ioVRefNum = fcbpb.ioFCBVRefNum; + pb.hFileInfo.ioDirID = fcbpb.ioFCBParID; + pb.hFileInfo.ioFDirIndex = 0; + + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + + pb.hFileInfo.ioFlCrDat -= gJanuaryFirst1970Seconds; + LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlCrDat); + LL_I2L(oneMillion, PR_USEC_PER_SEC); + LL_MUL(info->creationTime, oneMillion, dateInMicroSeconds); + + pb.hFileInfo.ioFlMdDat -= gJanuaryFirst1970Seconds; + LL_I2L(dateInMicroSeconds, pb.hFileInfo.ioFlMdDat); + LL_MUL(info->modifyTime, oneMillion, dateInMicroSeconds); + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRInt32 _MD_Stat(const char *path, struct stat *buf) +{ + OSErr err; + char *macFileName = NULL; + + err = ConvertUnixPathToMacPath(path, &macFileName); + if (err != noErr) + goto ErrorExit; + + err = stat(macFileName, buf); + if (err != noErr) + goto ErrorExit; + + PR_DELETE(macFileName); + + return 0; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return -1; +} + +PRStatus _MD_LockFile(PRInt32 fd) +{ + OSErr err; + FCBPBRec fcbpb; + HFileParam fpb; + Str255 pascalName; + + fcbpb.ioNamePtr = pascalName; + fcbpb.ioVRefNum = 0; + fcbpb.ioRefNum = fd; + fcbpb.ioFCBIndx = 0; + + err = PBGetFCBInfoSync(&fcbpb); + if (err != noErr) + goto ErrorExit; + + fpb.ioCompletion = NULL; + fpb.ioNamePtr = pascalName; + fpb.ioVRefNum = fcbpb.ioFCBVRefNum; + fpb.ioDirID = fcbpb.ioFCBParID; + + err = PBHSetFLockSync((HParmBlkPtr)&fpb); + if (err != noErr) + goto ErrorExit; + + return PR_SUCCESS; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return PR_FAILURE; +} + +PRStatus _MD_TLockFile(PRInt32 fd) +{ + return (_MD_LockFile(fd)); +} + +PRStatus _MD_UnlockFile(PRInt32 fd) +{ + OSErr err; + FCBPBRec fcbpb; + HFileParam fpb; + Str255 pascalName; + + fcbpb.ioNamePtr = pascalName; + fcbpb.ioVRefNum = 0; + fcbpb.ioRefNum = fd; + fcbpb.ioFCBIndx = 0; + + err = PBGetFCBInfoSync(&fcbpb); + if (err != noErr) + goto ErrorExit; + + fpb.ioCompletion = NULL; + fpb.ioNamePtr = pascalName; + fpb.ioVRefNum = fcbpb.ioFCBVRefNum; + fpb.ioDirID = fcbpb.ioFCBParID; + + err = PBHRstFLockSync((HParmBlkPtr)&fpb); + if (err != noErr) + goto ErrorExit; + + return PR_SUCCESS; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return PR_FAILURE; +} + +void SetLogFileTypeCreator(const char *logFile) +{ + HParamBlockRec pb; + OSErr err; + Str31 pName; + + PStrFromCStr(logFile, pName); + pb.fileParam.ioCompletion = nil; + pb.fileParam.ioNamePtr = pName; + pb.fileParam.ioVRefNum = 0; + pb.fileParam.ioFDirIndex = 0; + pb.fileParam.ioDirID = 0; + err = PBHGetFInfoSync(&pb); + PR_ASSERT(err == noErr); + + pb.fileParam.ioDirID = 0; + pb.fileParam.ioFlFndrInfo.fdType = 'TEXT'; + pb.fileParam.ioFlFndrInfo.fdCreator = 'ttxt'; + err = PBHSetFInfoSync(&pb); + PR_ASSERT(err == noErr); +} + +#if DEVELOPER_DEBUG +PR_IMPLEMENT (void) +SetupMacPrintfLog(char *logFile) +{ + /* + * We do _PR_InitLog() twice. The first to force the implicit initialization which + * will set logging to highest levels in _MD_EARLY_INIT. Then, change the env variable + * to disable kernel logging and call _PR_InitLog() again to make it effective. Since + * we are using logging to log test program output, we disable kernel logging to avoid + * all Kernel logging output. + */ +#ifdef PR_INTERNAL_LOGGING + _PR_InitLog(); + _MD_PutEnv("NSPR_LOG_MODULES=clock:0,cmon:0,io:0,mon:0,linker:0,cvar:0,sched:0,thread:0"); + _PR_InitLog(); +#endif + PR_ASSERT(PR_SetLogFile(logFile) == PR_TRUE); + + SetLogFileTypeCreator(logFile); +} +#endif + + +/* +********************** Old name related stuff that is unchanged. ********************** +*/ + +#if !defined(MAC_NSPR_STANDALONE) + +short GetVolumeRefNumFromName(const char *cTgtVolName) +{ + OSErr err; + Str32 pVolName; + char *cVolName = NULL; + HParamBlockRec hPB; + short refNum = 0; + + hPB.volumeParam.ioVolIndex = 0; + hPB.volumeParam.ioNamePtr = pVolName; + do { + hPB.volumeParam.ioVolIndex++; + err = PBHGetVInfoSync(&hPB); + CStrFromPStr(pVolName, &cVolName); + if (strcmp(cTgtVolName, cVolName) == 0) { + refNum = hPB.volumeParam.ioVRefNum; + PR_DELETE(cVolName); + break; + } + PR_DELETE(cVolName); + } while (err == noErr); + + return refNum; +} + +static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath) +{ + // Given a Unix style path with '/' directory separators, this allocates + // a path with Mac style directory separators in the path. + // + // It does not do any special directory translation; use ConvertUnixPathToMacPath + // for that. + + char *src; + char *tgt; + OSErr err = noErr; + + *macPath = malloc(strlen(unixPath) * 2); // Will be enough extra space. + require_action (*macPath != NULL, exit, err = memFullErr;); + + strcpy(*macPath, ""); // Clear the Mac path + + (const char *)src = unixPath; + tgt = *macPath; + + if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute + src++; // path, skip the separator + else + *(tgt++) = PR_PATH_SEPARATOR; + + if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with / + src += 2; // skip it. + + while (*src) + { // deal with the rest of the path + if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up? + *(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon. + src +=3; + } + else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator + *(tgt++) = PR_PATH_SEPARATOR; + src++; + } + else + *(tgt++) = *(src++); + } + + *tgt = NULL; // make sure itÕs null terminated. + +exit: + return err; +} + + +#include <Processes.h> + +static ProcessInfoRec gNavigatorProcInfo; +static FSSpec gGutsFolder; +static FSSpec gNetscapeFolder; + +static OSErr SetupRequiredFSSpecs(void) +{ + OSErr err; + CInfoPBRec pb; + ProcessSerialNumber curPSN = {0, kCurrentProcess}; + + gNavigatorProcInfo.processInfoLength = sizeof(ProcessInfoRec); + gNavigatorProcInfo.processName = NULL; + gNavigatorProcInfo.processAppSpec = &gNetscapeFolder; + + err = GetProcessInformation (&curPSN, &gNavigatorProcInfo); + if (err != noErr) + goto ErrorExit; + + /* guts folder resides at the same place as the app file itself */ + gGutsFolder = gNetscapeFolder; + /* How else do we do this hack??? + * Should NSPR have a string resource for this ? + */ + GetIndString( gGutsFolder.name, 300, 34); + + /* + * vRefNum and parentDirID are now set up correctly for the app file itself. + * parentDirID is the Netscape Folder's ID. Then Find it's parent ID to + * set up the FSSpec and its own name. + */ + + pb.dirInfo.ioCompletion = NULL; + pb.dirInfo.ioNamePtr = gNetscapeFolder.name; + pb.dirInfo.ioVRefNum = gNetscapeFolder.vRefNum; + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = gNetscapeFolder.parID; + + err = PBGetCatInfoSync(&pb); + if (err != noErr) + goto ErrorExit; + + gNetscapeFolder.parID = pb.dirInfo.ioDrParID; + + return noErr; + +ErrorExit: + return err; +} + +static OSErr FindGutsFolder(FSSpec *foundSpec) +{ + OSErr err; + + if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */ + err = SetupRequiredFSSpecs(); + if (err != noErr) + goto ErrorExit; + } + + *foundSpec = gGutsFolder; + + return noErr; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + return err; +} + +static OSErr FindNetscapeFolder(FSSpec *foundSpec) +{ + OSErr err; + + if (gNavigatorProcInfo.processInfoLength == 0) { /* Uninitialized? */ + err = SetupRequiredFSSpecs(); + if (err != noErr) + goto ErrorExit; + } + + *foundSpec = gNetscapeFolder; + + return noErr; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + return err; +} + + +PR_IMPLEMENT (OSErr) +ConvertUnixPathToMacPath(const char *unixPath, char **macPath) +{ + OSErr err = noErr; + + // ******** HACK ALERT ******** + // + // Java really wants long file names (>31 chars). We truncate file names + // greater than 31 characters long. Truncation is from the middle. + // + // Convert UNIX style path names (with . and / separators) into a Macintosh + // style path (with :). + // + // There are also a couple of special paths that need to be dealt with + // by translating them to the appropriate Mac special folders. These include: + // + // /usr/tmp/file => {TempFolder}file + // + // The file conversions we need to do are as follows: + // + // file => file + // dir/file => :dir:file + // ./file => file + // ../file => ::file + // ../dir/file => ::dir:file + // /file => ::BootDrive:file + // /dir/file => ::BootDrive:dir:file + + + if (!strcmp(unixPath, ".")) + { + *macPath = malloc(sizeof(":")); + if (*macPath == NULL) + err = memFullErr; + (*macPath)[0] = ':'; + (*macPath)[1] = '\0'; + } + else + + if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it. + err = CreateMacPathFromUnixPath(unixPath, macPath); + } + + else { + // WeÕre root-relative. This is either a special Unix directory, or a + // full path (which weÕll support on the Mac since they might be generated). + // This is not condoning the use of full-paths on the Macintosh for file + // specification. + + FSSpec foundSpec; + short pathBufferSize; + char *temp; + int tempLen; + + // Are we dealing with the temp folder? + if ((strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0) || + ((strncmp(unixPath, "/tmp", strlen("/tmp")) == 0))) { + CInfoPBRec pb; + + unixPath = PL_strchr(unixPath, PR_DIRECTORY_SEPARATOR); + if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) // skip past temp spec + unixPath += 5; + else + unixPath += 9; + + err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed + &foundSpec.vRefNum, &foundSpec.parID); + if (err == noErr) { + pb.dirInfo.ioCompletion = NULL; + pb.dirInfo.ioNamePtr = foundSpec.name; + pb.dirInfo.ioVRefNum = foundSpec.vRefNum; + pb.dirInfo.ioFDirIndex = -1; + pb.dirInfo.ioDrDirID = foundSpec.parID; + + err = PBGetCatInfoSync(&pb); + foundSpec.parID = pb.dirInfo.ioDrParID; + } + } + + else if (strncmp(unixPath, "/bin", strlen("/bin")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (strncmp(unixPath, "/dev", strlen("/dev")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (strncmp(unixPath, "/etc", strlen("/etc")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (!strncmp(unixPath, "/usr/local/netscape/", (tempLen = strlen("/usr/local/netscape/")))) { + + unixPath += tempLen; + + if (!strncmp(unixPath, "RequiredGuts/", (tempLen = strlen("RequiredGuts/")))) + { + unixPath += tempLen; + err = FindGutsFolder(&foundSpec); + } + else if (!strncmp(unixPath, "bin/", (tempLen = strlen("bin/")))) + { + unixPath += tempLen; + err = FindNetscapeFolder(&foundSpec); + } + else if (*unixPath == '\0') + { + // it's /usr/local/netscape + err = FindGutsFolder(&foundSpec); + } + + } + + else { + // This is a root relative directory, weÕll just convert the whole thing. + err = CreateMacPathFromUnixPath(unixPath, macPath); + goto Exit_ConvertUnixPathToMacPath; + } + + + + // WeÕre dealing with a special folder + if (err == noErr) + { + Handle hPathStr; + // Get the path to the root-relative directory + err = FSpGetFullPath(&foundSpec, &pathBufferSize, &hPathStr); // NewHandle's hPathStr + + if (noErr == err) + { + // convert handle to c-string + // add one for NULL termination + // pathBufferSize is now one greater than the length of the string + pathBufferSize++; + + *macPath = (char*) malloc(sizeof(char) * pathBufferSize); + (*macPath)[pathBufferSize - 1] = '\0'; + BlockMoveData(*hPathStr, *macPath, pathBufferSize - 1); + + DisposeHandle(hPathStr); + } + } + + if (err == noErr) + { + UInt32 unixPathLeft; + UInt32 macPathLen; + + unixPathLeft = strlen(unixPath); + macPathLen = strlen(*macPath); + + + // copy over the remaining file name, converting + if (pathBufferSize - 1 < macPathLen + unixPathLeft) + { + // need to grow string + *macPath = realloc(*macPath, macPathLen + unixPathLeft + 1); + err = (*macPath == NULL ? memFullErr : noErr); + } + + if (err == noErr) + { + // carefully remove the '/''s out of the unix path. If we see an "escaped" / + // we will leave it in there, otherwise we take it out and replace it with a : + // we have to do this before we convert to a mac-path, so we can tell what is + // really a path separator and what is in the name of a file or directory + // Make sure that all of the /Õs are :Õs in the final pathname + // effectively we do a + // strcat(*macPath, unixPath); while replace all occurrences of / with : in unixPath + char* dp; + const char* sp; + + sp = unixPath; + dp = *macPath + macPathLen; + + for (;*sp != '\0'; sp++, dp++) + { + if (*sp == PR_DIRECTORY_SEPARATOR) + { + // if we can look at the previous character + if (sp > unixPath) + { + // check to see if previous character is an escape + if (sp[-1] == '\\') + { + // leave it in, and cycle + continue; + } + else + { + *dp = PR_PATH_SEPARATOR; + } + } + else + *dp = PR_PATH_SEPARATOR; + } + else + { + // just copy; + *dp = *sp; + } + } + + *dp = '\0'; // NULL terminate *macPath + } +#if DEBUG + // we used to check here, now we check above, we leave this in + // the debug build to make sure we didn't screw up + // Make sure that all of the /Õs are :Õs in the final pathname + for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) { + + if (*temp == PR_DIRECTORY_SEPARATOR) + { + DebugStr("\pFound a slash"); + *temp = PR_PATH_SEPARATOR; + } + } +#endif + } + } + + +Exit_ConvertUnixPathToMacPath: + + return err; +} + +// Hey! Before you delete this "hack" you should look at how it's being +// used by sun-java/netscape/applet/appletStubs.c. +PR_IMPLEMENT (OSErr) +ConvertMacPathToUnixPath(const char *macPath, char **unixPath) +{ + // *** HACK *** + // Get minimal version working + + char *unixPathPtr; + + *unixPath = malloc(strlen(macPath) + 2); // Add one for the front slash, one for null + if (*unixPath == NULL) + return (memFullErr); + + unixPathPtr = *unixPath; + + *unixPathPtr++ = PR_DIRECTORY_SEPARATOR; + + do { + // Translate all colons to slashes + if (*macPath == PR_PATH_SEPARATOR) + *unixPathPtr = PR_DIRECTORY_SEPARATOR; + else + *unixPathPtr = *macPath; + + unixPathPtr++; + macPath++; + } while (*macPath != NULL); + + // Terminate the string + *unixPathPtr = '\0'; + + return (noErr); +} + +OSErr +ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec) +{ + char* macPath; + OSErr convertError; + int len; + + convertError = ConvertUnixPathToMacPath(unixPath, &macPath); + if (convertError != noErr) + return convertError; + + len = strlen(macPath); + + if (*macPath == PR_PATH_SEPARATOR) + { + if (len < sizeof(Str255)) + { + short vRefNum; + long dirID; + Str255 pascalMacPath; + + convertError = HGetVol(NULL, &vRefNum, &dirID); + if (convertError == noErr) + { + PStrFromCStr(macPath, pascalMacPath); + convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec); + } + } + else + convertError = paramErr; + } + else + { + convertError = FSpLocationFromFullPath(len, macPath, fileSpec); + if (convertError == fnfErr) + { + CInfoPBRec pb; + Str255 pascalMacPath; + OSErr err; + + PStrFromCStr(macPath, pascalMacPath); + /* + FSpLocationFromFullPath does not work for directories unless there is + a ":" at the end. We will make sure of an existence of a directory. + If so, the returned fileSpec is valid from FSpLocationFromFullPath eventhough + it returned an error. + */ + pb.hFileInfo.ioNamePtr = pascalMacPath; + pb.hFileInfo.ioVRefNum = 0; + pb.hFileInfo.ioDirID = 0; + pb.hFileInfo.ioFDirIndex = 0; + + err = PBGetCatInfoSync(&pb); + if (err == noErr) + convertError = noErr; + } + } + + free(macPath); + + return (convertError); +} + + +FILE *_OS_FOPEN(const char *filename, const char *mode) +{ + OSErr err = noErr; + char *macFileName = NULL; + FILE *result; + + err = ConvertUnixPathToMacPath(filename, &macFileName); + if (err != noErr) + goto ErrorExit; + + result = fopen(macFileName, mode); + + PR_DELETE(macFileName); + + return result; + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + _MD_SetError(err); + return NULL; +} + +#else + +short GetVolumeRefNumFromName(const char *cTgtVolName) +{ + OSErr err; + Str32 pVolName; + char *cVolName = NULL; + HParamBlockRec hPB; + short refNum = 0; + + hPB.volumeParam.ioVolIndex = 0; + hPB.volumeParam.ioNamePtr = pVolName; + do { + hPB.volumeParam.ioVolIndex++; + err = PBHGetVInfoSync(&hPB); + CStrFromPStr(pVolName, &cVolName); + if (strcmp(cTgtVolName, cVolName) == 0) { + refNum = hPB.volumeParam.ioVRefNum; + PR_DELETE(cVolName); + break; + } + PR_DELETE(cVolName); + } while (err == noErr); + + return refNum; +} + + + +static OSErr GetFullPath(short vRefNum, long dirID, char **fullPath, int *strSize) +{ + Str255 pascalDirName; + char cDirName[256]; + char *tmpPath = NULL; // needed since sprintf isnÕt safe + CInfoPBRec myPB; + OSErr err = noErr; + + + // get the full path of the temp folder. + *strSize = 256; + *fullPath = NULL; + *fullPath = malloc(*strSize); // How big should this thing be? + require_action (*fullPath != NULL, errorExit, err = memFullErr;); + + tmpPath = malloc(*strSize); + require_action (tmpPath != NULL, errorExit, err = memFullErr;); + + strcpy(*fullPath, ""); // Clear C result + strcpy(tmpPath, ""); + pascalDirName[0] = 0; // Clear Pascal intermediate string + + myPB.dirInfo.ioNamePtr = &pascalDirName[0]; + myPB.dirInfo.ioVRefNum = vRefNum; + myPB.dirInfo.ioDrParID = dirID; + myPB.dirInfo.ioFDirIndex = -1; // Getting info about + + do { + myPB.dirInfo.ioDrDirID = myPB.dirInfo.ioDrParID; + + err = PBGetCatInfoSync(&myPB); + require(err == noErr, errorExit); + + // Move the name into C domain + memcpy(&cDirName, &pascalDirName, 256); + p2cstr((unsigned char *)&cDirName); // Changes in place! + + if ((strlen(cDirName) + strlen(*fullPath)) > *strSize) { + // We need to grow the string, do it in 256 byte chunks + (*strSize) += 256; + *fullPath = PR_REALLOC(*fullPath, *strSize); + require_action (*fullPath != NULL, errorExit, err = memFullErr;); + + tmpPath = PR_REALLOC(tmpPath, *strSize); + require_action (tmpPath != NULL, errorExit, err = memFullErr;); + } + sprintf(tmpPath, "%s:%s", cDirName, *fullPath); + strcpy(*fullPath, tmpPath); + } while (myPB.dirInfo.ioDrDirID != fsRtDirID); + + PR_DELETE(tmpPath); + + return noErr; + + +errorExit: + PR_DELETE(*fullPath); + PR_DELETE(tmpPath); + + return err; + +} + +static OSErr CreateMacPathFromUnixPath(const char *unixPath, char **macPath) +{ + // Given a Unix style path with '/' directory separators, this allocates + // a path with Mac style directory separators in the path. + // + // It does not do any special directory translation; use ConvertUnixPathToMacPath + // for that. + + char *src; + char *tgt; + OSErr err = noErr; + + *macPath = malloc(strlen(unixPath) * 2); // Will be enough extra space. + require_action (*macPath != NULL, exit, err = memFullErr;); + + strcpy(*macPath, ""); // Clear the Mac path + + (const char *)src = unixPath; + tgt = *macPath; + + if (PL_strchr(src, PR_DIRECTORY_SEPARATOR) == src) // If weÕre dealing with an absolute + src++; // path, skip the separator + else + *(tgt++) = PR_PATH_SEPARATOR; + + if (PL_strstr(src, UNIX_THIS_DIRECTORY_STR) == src) // If it starts with ./ + src += 2; // skip it. + + while (*src) + { // deal with the rest of the path + if (PL_strstr(src, UNIX_PARENT_DIRECTORY_STR) == src) { // Going up? + *(tgt++) = PR_PATH_SEPARATOR; // simply add an extra colon. + src +=3; + } + else if (*src == PR_DIRECTORY_SEPARATOR) { // Change the separator + *(tgt++) = PR_PATH_SEPARATOR; + src++; + } + else + *(tgt++) = *(src++); + } + + *tgt = NULL; // make sure itÕs null terminated. + +exit: + return err; +} + +static OSErr ConvertUnixPathToMacPath(const char *unixPath, char **macPath) +{ + OSErr err = noErr; + + + // + // Convert UNIX style path names (with . and / separators) into a Macintosh + // style path (with :). + // + // There are also a couple of special paths that need to be dealt with + // by translating them to the appropriate Mac special folders. These include: + // + // /usr/tmp/file => {TempFolder}file + // + // The file conversions we need to do are as follows: + // + // file => file + // dir/file => :dir:file + // ./file => file + // ../file => ::file + // ../dir/file => ::dir:file + // /file => ::BootDrive:file + // /dir/file => ::BootDrive:dir:file + + + if (*unixPath != PR_DIRECTORY_SEPARATOR) { // Not root relative, just convert it. + err = CreateMacPathFromUnixPath(unixPath, macPath); + } + + else { + // WeÕre root-relative. This is either a special Unix directory, or a + // full path (which weÕll support on the Mac since they might be generated). + // This is not condoning the use of full-paths on the Macintosh for file + // specification. + + short foundVRefNum; + long foundDirID; + int pathBufferSize; + char *temp; + char isNetscapeDir = false; + + // Are we dealing with the temp folder? + if (strncmp(unixPath, "/usr/tmp", strlen("/usr/tmp")) == 0){ + unixPath += 8; + if (*unixPath == PR_DIRECTORY_SEPARATOR) + unixPath++; // Skip the slash + err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed + &foundVRefNum, &foundDirID); + } + + if (strncmp(unixPath, "/tmp", strlen("/tmp")) == 0) { + unixPath += 4; // Skip the slash + if (*unixPath == PR_DIRECTORY_SEPARATOR) + unixPath++; // Skip the slash + err = FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder, // Create if needed + &foundVRefNum, &foundDirID); + } + + else if (strncmp(unixPath, "/bin", strlen("/bin")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (strncmp(unixPath, "/dev", strlen("/dev")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (strncmp(unixPath, "/etc", strlen("/etc")) == 0) { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + else if (strncmp(unixPath, "/usr", strlen("/usr")) == 0) { + + int usrNetscapePathLen; + + usrNetscapePathLen = strlen("/usr/local/netscape/"); + + if (strncmp(unixPath, "/usr/local/netscape/", usrNetscapePathLen) == 0) { + unixPath += usrNetscapePathLen; +// err = FindPreferencesFolder(&foundVRefNum, &foundDirID); + err = paramErr; + isNetscapeDir = true; + } + + else { + dprintf("Unable to translate Unix file path %s to Mac path\n", unixPath); + err = -1; + goto Exit_ConvertUnixPathToMacPath; + } + + } + + else { + // This is a root relative directory, weÕll just convert the whole thing. + err = CreateMacPathFromUnixPath(unixPath, macPath); + goto Exit_ConvertUnixPathToMacPath; + } + + // WeÕre dealing with a special folder + if (err == noErr) + // Get the path to the root-relative directory + err = GetFullPath(foundVRefNum, foundDirID, macPath, &pathBufferSize); // mallocs macPath + + if (err == noErr){ + + // copy over the remaining file name, converting + if (pathBufferSize < (strlen(*macPath) + strlen(unixPath))) { + // need to grow string + *macPath = PR_REALLOC(*macPath, (strlen(*macPath) + strlen(unixPath) + + (isNetscapeDir ? strlen("Netscape Ä:") : 0))); + err = (*macPath == NULL ? memFullErr : noErr); + } + + if (isNetscapeDir) + strcat(*macPath, "Netscape Ä:"); + + if (err == noErr) + strcat(*macPath, unixPath); + + // Make sure that all of the /Õs are :Õs in the final pathname + + for (temp = *macPath + strlen(*macPath) - strlen(unixPath); *temp != '\0'; temp++) { + if (*temp == PR_DIRECTORY_SEPARATOR) + *temp = PR_PATH_SEPARATOR; + } + + } + } + + +Exit_ConvertUnixPathToMacPath: + + return err; +} + +OSErr +ConvertUnixPathToFSSpec(const char *unixPath, FSSpec *fileSpec) +{ + char* macPath; + OSErr convertError; + int len; + + convertError = ConvertUnixPathToMacPath(unixPath, &macPath); + if (convertError != noErr) + return convertError; + + len = strlen(macPath); + + if (*macPath == PR_PATH_SEPARATOR) + { + if (len < sizeof(Str255)) + { + short vRefNum; + long dirID; + Str255 pascalMacPath; + + convertError = HGetVol(NULL, &vRefNum, &dirID); + if (convertError == noErr) + { + PStrFromCStr(macPath, pascalMacPath); + convertError = FSMakeFSSpec(vRefNum, dirID, pascalMacPath, fileSpec); + } + } + else + convertError = paramErr; + } + else + { + convertError = FSpLocationFromFullPath(len, macPath, fileSpec); + } + + free(macPath); + + return (convertError); +} + + +#endif + +/* + ********************************************************************** + * + * Memory-mapped files are not implementable on the Mac. + * + ********************************************************************** + */ + +PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) +{ +#pragma unused (fmap, size) + + PR_ASSERT(!"Not implemented"); + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +void * _MD_MemMap( + PRFileMap *fmap, + PRInt64 offset, + PRUint32 len) +{ +#pragma unused (fmap, offset, len) + + PR_ASSERT(!"Not implemented"); + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PRStatus _MD_MemUnmap(void *addr, PRUint32 len) +{ +#pragma unused (addr, len) + + PR_ASSERT(!"Not implemented"); + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PRStatus _MD_CloseFileMap(PRFileMap *fmap) +{ +#pragma unused (fmap) + + PR_ASSERT(!"Not implemented"); + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} diff --git a/pr/src/md/mac/macsockclient.c b/pr/src/md/mac/macsockclient.c new file mode 100644 index 00000000..3ae9b0e5 --- /dev/null +++ b/pr/src/md/mac/macsockclient.c @@ -0,0 +1,558 @@ +/* -*- 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 <errno.h> + +#define OTUNIXERRORS 1 /* We want OpenTransport error codes */ +#include <OpenTransport.h> +#include <OpenTptInternet.h> // All the internet typedefs + +#include "macsock.h" /* from macsock library */ +#include "primpl.h" + +void _MD_InitNetAccess() +{ +} + +static void macsock_map_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EINPROGRESS: + PR_SetError(PR_IN_PROGRESS_ERROR, err); + break; + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EAFNOSUPPORT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case ENETUNREACH: + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + /* + * UNIX domain sockets are not supported in NSPR + */ + case EACCES: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ENXIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EPROTOTYPE: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +// Errors returned: +// ENETDOWN - no MacTCP driver +// EPROTONOSUPPORT - bad socket type/protocol +// ENOBUFS - not enough space for another socket, or failure in socket creation routine +PRInt32 _MD_socket(int domain, int type, int protocol) +{ + int err; + + err = macsock_socket(domain, type, protocol); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF -- bad socket id +// EFAULT -- bad address format +PRInt32 _MD_bind(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_bind(sID, (const struct sockaddr *)addr, addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF -- bad socket id +// EOPNOTSUPP -- socket is already connected, and closing +// EISCONN -- already connected +// EINPROGRESS -- connecting right now +PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_listen(sID, backlog); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF -- bad socket id +PRInt32 _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_getsockname(sID, (struct sockaddr *)addr, (int *)addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF - bad socket id +// ENOPROTOOPT - The option is unknown +PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_getsockopt(sID, level, optname, optval, optlen); + if (err == -1) { + macsock_map_error(errno); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +// Errors: +// EBADF - bad socket id +// ENOTCONN - socket hasnÕt been properly created +PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_setsockopt(sID, level, optname, optval, optlen); + if (err == -1) { + macsock_map_error(errno); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +PRInt32 _MD_socketavailable(PRFileDesc *fd, size_t *bytes) +{ + int err; + int sID = fd->secret->md.osfd; + + // Careful of the return value here. 0 => failure, 1 => success + err = macsock_socketavailable(sID, bytes); + if (err == 0) { + _PR_MD_CURRENT_THREAD()->md.osErrCode = -1; + return -1; + } + + return 0; +} + +PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_accept(sID, (struct sockaddr *)addr, (int *)addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Connect +// check the arguments validity +// issue the connect call to the stream +// Errors: +// EBADF -- bad socket id, bad MacTCP stream +// EAFNOSUPPORT -- bad address format +// EADDRINUSE -- we are listening, or duplicate socket +// EINPROGRESS -- we are connecting right now +// EISCONN -- already connected +// ECONNREFUSED -- other side has closed, or open has failed +// EALREADY -- we are connected +// EINTR -- user interrupted +PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_connect(sID, (struct sockaddr *)addr, addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF - bad socket ID +// ENOTCONN - no such connection +PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_recv(sID, buf, amount, flags); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount, PRIntn flags, PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_send(sID, buf, amount, flags); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_recvfrom(sID, buf, amount, flags, (struct sockaddr *)addr, (int *)addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + + +PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout) +{ +#pragma unused (timeout) + + int err; + int sID = fd->secret->md.osfd; + + err = macsock_sendto(sID, buf, amount, flags, (struct sockaddr *)addr, addrlen); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +// Errors: +// EBADF -- bad socket id +PRInt32 _MD_closesocket(PRInt32 osfd) +{ + int err; + int sID = osfd; + + err = macsock_close(sID); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +PRInt32 _MD_writev(PRFileDesc *fd, struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) +{ +#pragma unused (fd, iov, iov_size, timeout) + + PR_ASSERT(0); + _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr; + return -1; +} + +PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_shutdown(sID, how); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + +PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ + int err; + int sID = fd->secret->md.osfd; + + err = macsock_getpeername(sID, (struct sockaddr *)addr, (int *)addrlen); + if (err == -1) { + macsock_map_error(errno); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +void _MD_makenonblock(PRFileDesc *fd) +{ + int err; + int sID = fd->secret->md.osfd; + int optval =1; + + err = macsock_setsockopt(sID, SOL_SOCKET, FIONBIO, (const void *)&optval, 0); + if (err == -1) { + macsock_map_error(errno); + } +} + +struct hostent *gethostbyname(const char * name) +{ + return macsock_gethostbyname((char *)name); +} + +struct hostent *gethostbyaddr(const void *addr, int addrlen, int type) +{ + return macsock_gethostbyaddr(addr, addrlen, type); +} + +PRStatus _MD_gethostname(char *name, int namelen) +{ + int err; + + err = macsock_gethostname(name, namelen); + if (err != noErr) { + macsock_map_error(err); + return PR_FAILURE; + } + + return PR_SUCCESS; +} + +#define kIPName "ip" +static struct protoent sIPProto = {kIPName, NULL, INET_IP}; +static struct protoent sTCPProto = {kTCPName, NULL, INET_TCP}; +static struct protoent sUDPProto = {kUDPName, NULL, INET_UDP}; + +struct protoent *getprotobyname(const char * name) +{ + if (strcmp(name, kIPName) == 0) + return (&sIPProto); + + if (strcmp(name, kTCPName) == 0) + return (&sTCPProto); + + if (strcmp(name, kUDPName) == 0) + return (&sUDPProto); + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = paramErr; + return NULL; +} + + +struct protoent *getprotobynumber(int number) +{ + if (number == INET_IP) + return (&sIPProto); + + if (number == INET_TCP) + return (&sTCPProto); + + if (number == INET_UDP) + return (&sUDPProto); + +ErrorExit: + _PR_MD_CURRENT_THREAD()->md.osErrCode = paramErr; + return NULL; +} + +PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRInt32 n; + + fd_set rd, wt, ex; + struct timeval tv, *tvp = NULL; + int maxfd = -1; + + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); + + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRInt32 osfd; + PRInt16 in_flags = pd->in_flags; + PRFileDesc *bottom = pd->fd; + + if (NULL == bottom) { + continue; + } + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + osfd = bottom->secret->md.osfd; + + if (osfd > maxfd) { + maxfd = 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; + } + + n = select(maxfd + 1, &rd, &wt, &ex, tvp); + + if (n > 0) { + n = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRInt32 osfd; + PRInt16 in_flags = pd->in_flags; + PRInt16 out_flags = 0; + PRFileDesc *bottom = pd->fd; + + if (NULL == bottom) { + continue; + } + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + osfd = 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++; + } + } + /* + Can't do this assert because MacSock returns write fds even if we did not + set it in the original write fd set. + */ + /*PR_ASSERT(n > 0);*/ + } else + PR_ASSERT(n == 0); + + return n; +} + +int _MD_mac_get_nonblocking_connect_error(int osfd) +{ + int err; + + err = macsock_getconnectstatus (osfd); + if (err == -1) { + macsock_map_error(errno); + return -1; + } + + return err; +} + diff --git a/pr/src/md/mac/macsocket.h b/pr/src/md/mac/macsocket.h new file mode 100644 index 00000000..4bc9ed66 --- /dev/null +++ b/pr/src/md/mac/macsocket.h @@ -0,0 +1,219 @@ +/* -*- 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. + */ + +#ifndef macksocket_h___ +#define macksocket_h___ + +// macsock.h +// Interface visible to xp code +// C socket type definitions and routines +// from sys/socket.h +#include <OpenTptInternet.h> // All the internet typedefs +#include <utime.h> // For timeval +/* + * sleep and delay conflict with the same in unistd.h from Metrowerks. OT + * defines them as + * + * extern pascal void OTDelay(UInt32 seconds); + * extern pascal void OTIdle(void); + * + * #define sleep(x) OTDelay(x) + * #define delay(x) OTDelay(x) + */ + +#undef sleep +#undef delay + +#pragma once + +#include "prio.h" + +struct sockaddr { + unsigned char sa_len; /* total length */ + unsigned char sa_family; /* address family */ + char sa_data[14]; /* actually longer; address value */ +}; + +// from netinet/in.h +struct in_addr { + unsigned long s_addr; +}; + +struct sockaddr_in { + unsigned char sin_len; + unsigned char sin_family; // AF_INET + unsigned short sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses from name server */ +#define h_addr h_addr_list[0] /* address, for backward compatiblity */ +}; + +// Necessary network defines, found by grepping unix headers when XP code would not compile +#define FIONBIO 1 +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 +#define IPPROTO_TCP INET_TCP // Default TCP protocol +#define IPPROTO_UDP INET_UDP // Default UDP protocol +#define INADDR_ANY kOTAnyInetAddress +#define SOL_SOCKET XTI_GENERIC // Any type of socket +#define SO_REUSEADDR IP_REUSEADDR +#define MSG_PEEK 0x2 // Just look at a message waiting, donÕt actually read it. + +typedef unsigned long u_long; + +/* ldap.h has its own definition of fd_set */ +/* select support */ +#if !defined(FD_SET) +#define NBBY 8 +typedef long fd_mask; +#define NFDBITS (sizeof(fd_mask) * NBBY) /* bits per mask */ + +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) +#endif +#define FD_SETSIZE 64 +typedef struct fd_set{ + fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} fd_set; + +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) memset (p, 0, sizeof(*(p))) +#endif /* !FD_SET */ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern unsigned long inet_addr(const char *cp); +extern char *inet_ntoa(struct in_addr in); + +inline unsigned long htonl(unsigned long hostlong) {return hostlong;} +inline unsigned long ntohl(unsigned long netlong) {return netlong;} +inline unsigned short ntohs(unsigned short netshort) {return netshort;} +inline unsigned short htons(unsigned short hostshort) {return hostshort;} + + +// UNIX look-alike routines +// They make sure that the arguments passed in are valid, and then +// +extern struct hostent *macsock_gethostbyaddr(const void *addr, int addrlen, int type); + +extern int macsock_socket(int domain, int type, int protocol); +extern int macsock_ioctl(int sID, unsigned int request, void *value); +extern int macsock_connect(int sID, struct sockaddr *name, int namelen); +extern int macsock_write(int sID, const void *buffer, unsigned buflen); +extern int macsock_read(int sID, void *buf, unsigned nbyte); +extern int macsock_close(int sID); + +extern int macsock_accept(int sID, struct sockaddr *addr, int *addrlen); +extern int macsock_bind(int sID, const struct sockaddr *name, int namelen); +extern int macsock_listen(int sID, int backlog); + +extern int macsock_shutdown(int sID, int how); +extern int macsock_getpeername(int sID, struct sockaddr *name, int *namelen); +extern int macsock_getsockname(int sID, struct sockaddr *name, int *namelen); +extern int macsock_getsockopt(int sID, int level, int optname, void *optval,int *optlen); +extern int macsock_setsockopt(int sID, int level, int optname, const void *optval,int optlen); +extern int macsock_socketavailable(int sID, size_t *bytesAvailable); +extern int macsock_dup(int sID); + +extern int macsock_send(int sID, const void *msg, int len, int flags); +extern int macsock_sendto(int sID, const void *msg, int len, int flags, struct sockaddr *toAddr, int toLen); +extern int macsock_recvfrom(int sID, void *buf, int len, int flags, struct sockaddr *from, int *fromLen); +extern int macsock_recv(int sID, void *buf, int len, int flags); + +extern int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); + + +#define macsock_gethostbyaddr PR_GetHostByAddr +#define macsock_socket PR_Socket +#define macsock_connect PR_Connect +#define macsock_write PR_Write +#define macsock_read PR_Read +#define macsock_close PR_Close +#define macsock_accept PR_Accept +#define macsock_bind PR_Bind +#define macsock_listen PR_Listen +#define macsock_shutdown PR_Shutdown +#define macsock_getpeername PR_GetPeerName +#define macsock_getsockname PR_GetSockName +#define macsock_getsockopt PR_GetSockOpt +#define macsock_setsockopt PR_SetSockOpt +#define macsock_socketavailable PR_SocketAvailable +#define macsock_send PR_Send +#define macsock_sendto PR_SendTo +#define macsock_recvfrom PR_RecvFrom +#define macsock_recv PR_Recv + +#ifdef __cplusplus +} +#endif +//extern int errno; + +/* +macsock_sendmsg +macsock_readv +macsock_writev +*/ + +/* New definitions that are not defined in macsock.h in macsock library */ +struct protoent { + char *p_name; /* official protocol name */ + char **p_aliases; /* alias list */ + int p_proto; /* protocol # */ +}; + +extern struct protoent *getprotobyname(const char * name); +extern struct protoent *getprotobynumber(int number); + +extern int gethostname (char *name, int namelen); +extern struct hostent *gethostbyname(const char * name); +extern struct hostent *gethostbyaddr(const void *addr, int addrlen, int type); + +#define INADDR_LOOPBACK 0x7F000001 + +#define SO_KEEPALIVE TCP_KEEPALIVE +#define SO_RCVBUF XTI_RCVBUF +#define SO_SNDBUF XTI_SNDBUF +#define SO_LINGER XTI_LINGER /* linger on close if data present */ + +#define IPPROTO_IP INET_IP + +/* Get/Set sock opt until fixed in NSPR 2.0 */ +struct linger { + int l_onoff; /* option on/off */ + int l_linger; /* linger time */ +}; + +struct ip_mreq { + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_interface; /* local IP address of interface */ +}; + +#endif /* macksocket_h___ */ diff --git a/pr/src/md/mac/macsockotpt.c b/pr/src/md/mac/macsockotpt.c new file mode 100644 index 00000000..4c74bb8d --- /dev/null +++ b/pr/src/md/mac/macsockotpt.c @@ -0,0 +1,1542 @@ +/* -*- 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 turns on UNIX style errors in OT 1.1 headers */ +#define OTUNIXERRORS 1 + +#include <Gestalt.h> + +/* + Since Apple put out new headers without + putting in a way to test for them, we found some random symbol which + isn't defined in the "1.1" headers. +*/ +#include <OpenTransport.h> +#ifdef kOTInvalidStreamRef +/* old */ +#define GESTALT_OPEN_TPT_PRESENT gestaltOpenTptPresent +#define GESTALT_OPEN_TPT_TCP_PRESENT gestaltOpenTptTCPPresent +#else +/* new */ +#define GESTALT_OPEN_TPT_PRESENT gestaltOpenTptPresentMask +#define GESTALT_OPEN_TPT_TCP_PRESENT gestaltOpenTptTCPPresentMask +#endif + +#include <OpenTptInternet.h> // All the internet typedefs +#include "macsocket.h" +#include "primpl.h" + +typedef enum SndRcvOpCode { + kSTREAM_SEND, + kSTREAM_RECEIVE , + kDGRAM_SEND, + kDGRAM_RECEIVE +} SndRcvOpCode; + + +static InetSvcRef sSvcRef; + +static pascal void NotifierRoutine(void * contextPtr, OTEventCode code, + OTResult result, void * cookie); + +static PRBool GetState(EndpointRef endpoint, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady); + +extern void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout); +extern void DoneWaitingOnThisThread(PRThread *thread); + +void _MD_InitNetAccess() +{ + OSErr err; + OSStatus errOT; + PRBool hasOTTCPIP = PR_FALSE; + PRBool hasOT = PR_FALSE; + long gestaltResult; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + err = Gestalt(gestaltOpenTpt, &gestaltResult); + if (err == noErr) + if (gestaltResult & GESTALT_OPEN_TPT_PRESENT) + hasOT = PR_TRUE; + + if (hasOT) + if (gestaltResult & GESTALT_OPEN_TPT_TCP_PRESENT) + hasOTTCPIP = PR_TRUE; + + PR_ASSERT(hasOTTCPIP == PR_TRUE); + + errOT = InitOpenTransport(); + PR_ASSERT(err == kOTNoError); + + sSvcRef = OTOpenInternetServices(kDefaultInternetServicesPath, NULL, &errOT); + if (errOT != kOTNoError) return; /* no network -- oh well */ + PR_ASSERT((sSvcRef != NULL) && (errOT == kOTNoError)); + + /* Install notify function for DNR Address To String completion */ + errOT = OTInstallNotifier(sSvcRef, NotifierRoutine, me); + PR_ASSERT(errOT == kOTNoError); + + /* Put us into async mode */ + errOT = OTSetAsynchronous(sSvcRef); + PR_ASSERT(errOT == kOTNoError); + +/* XXX Does not handle absence of open tpt and tcp yet! */ +} + +static void macsock_map_error(OSStatus err) +{ + _PR_MD_CURRENT_THREAD()->md.osErrCode = err; + + if (IsEError(err) || (err >= EPERM && err <= ELASTERRNO)) { + switch (IsEError(err) ? OSStatus2E(err) : err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EINPROGRESS: + PR_SetError(PR_IN_PROGRESS_ERROR, err); + break; + case EWOULDBLOCK: + case EAGAIN: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case ENETUNREACH: + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ENXIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EPROTOTYPE: + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_OPERATION_NOT_SUPPORTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } + } else { + PR_ASSERT(IsXTIError(err)); + switch (err) { + case kOTNoDataErr: + case kOTFlowErr: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + default: + PR_ASSERT(0); + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } + } +} + +static void PrepareThreadForAsyncIO(PRThread *thread, EndpointRef endpoint, PRInt32 osfd) +{ + OSStatus err; + + thread->io_pending = PR_TRUE; + thread->io_fd = osfd; + thread->md.osErrCode = noErr; + + OTRemoveNotifier(endpoint); + err = OTInstallNotifier(endpoint, NotifierRoutine, thread); + PR_ASSERT(err == kOTNoError); +} + +// Notification routine +// Async callback routine. +// A5 is OK. Cannot allocate memory here +pascal void NotifierRoutine(void * contextPtr, OTEventCode code, OTResult result, void * cookie) +{ + PRThread * thread = (PRThread *) contextPtr; + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + switch (code) + { +// Async Completion Event + case T_OPENCOMPLETE: + case T_BINDCOMPLETE: + case T_UNBINDCOMPLETE: + case T_GETPROTADDRCOMPLETE: + case T_ACCEPTCOMPLETE: +// Connect callback + case T_CONNECT: +// Standard or expedited data is available + case T_DATA: + case T_EXDATA: +// Standard or expedited data Flow control lifted + case T_GODATA: + case T_GOEXDATA: +// Asynchronous Listen Event + case T_LISTEN: +// DNR String To Address Complete Event + case T_DNRSTRINGTOADDRCOMPLETE: +// Option Management Request Complete Event + case T_OPTMGMTCOMPLETE: + thread->md.osErrCode = result; + thread->md.cookie = cookie; + if (_PR_MD_GET_INTSOFF()) { + cpu->u.missed[cpu->where] |= _PR_MISSED_IO; + thread->md.notifyPending = PR_TRUE; + return; + } + DoneWaitingOnThisThread(thread); + break; + +// T_ORDREL orderly release is available; nothing to do + case T_ORDREL: + break; + +// T_PASSCON; nothing to do + case T_PASSCON: + break; + +// T_DISCONNECT; disconnect is available; nothing to do + case T_DISCONNECT: + break; + +// UDP Send error; clear the error + case T_UDERR: + (void) OTRcvUDErr((EndpointRef) cookie, NULL); + default: + PR_ASSERT(0); + } +} + + +static OSErr CreateSocket(int type, EndpointRef *endpoint) +{ + OSStatus err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + switch (type){ + case SOCK_STREAM: + err = OTAsyncOpenEndpoint(OTCreateConfiguration(kTCPName), 0, NULL, + NotifierRoutine, me); + break; + case SOCK_DGRAM: + err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, + NotifierRoutine, me); + break; + } + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + *endpoint = me->md.cookie; + PR_ASSERT(*endpoint != NULL); + + return kOTNoError; + +ErrorExit: + return err; +} + + +// Errors returned: +// kOTXXXX - OT returned error +// EPROTONOSUPPORT - bad socket type/protocol +// ENOBUFS - not enough space for another socket, or failure in socket creation routine +PRInt32 _MD_socket(int domain, int type, int protocol) +{ + OSStatus err; + EndpointRef endpoint; + + // We only deal with internet domain + if (domain != AF_INET) { + err = kEPROTONOSUPPORTErr; + goto ErrorExit; + } + + // We only know about tcp & udp + if ((type != SOCK_STREAM) && (type != SOCK_DGRAM)) { + err = kEPROTONOSUPPORTErr; + goto ErrorExit; + } + + // Convert default types to specific types. + if (protocol == 0) { + if (type == SOCK_DGRAM) + protocol = IPPROTO_UDP; + else if (type == SOCK_STREAM) + protocol = IPPROTO_TCP; + } + + // Only support default protocol for tcp + if ((type == SOCK_STREAM) && (protocol != IPPROTO_TCP)) { + err = kEPROTONOSUPPORTErr; + goto ErrorExit; + } + + // Only support default protocol for udp + if ((type == SOCK_DGRAM) && (protocol != IPPROTO_UDP)) { + err = kEPROTONOSUPPORTErr; + goto ErrorExit; + } + + // Create a socket, we might run out of memory + err = CreateSocket(type, &endpoint); + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT((PRInt32)endpoint != -1); + + return ((PRInt32)endpoint); + +ErrorExit: + macsock_map_error(err); + return -1; +} + +// Errors: +// EBADF -- bad socket id +// EFAULT -- bad address format +PRInt32 _MD_bind(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + TBind bindReq; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRUint32 retryCount = 0; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (addr == NULL) { + err = kEFAULTErr; + goto ErrorExit; + } + + // setup our request +#if 0 + if ((addr->inet.port == 0) || (addr->inet.ip == 0)) + bindReq.addr.len = 0; + else +#endif +/* + * There seems to be a bug with OT ralted to OTBind failing with kOTNoAddressErr eventhough + * a proper legal address was supplied. This happens very rarely and just retrying the + * operation after a certain time (less than 1 sec. does not work) seems to succeed. + */ + +TryAgain: + bindReq.addr.len = addrlen; + + bindReq.addr.maxlen = addrlen; + bindReq.addr.buf = (UInt8*) addr; + bindReq.qlen = 1; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTBind(endpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie == NULL); + + return kOTNoError; + +ErrorExit: + if ((err == kOTNoAddressErr) && (++retryCount <= 4)) { + long finalTicks; + + Delay(100,&finalTicks); + goto TryAgain; + } + macsock_map_error(err); + return -1; +} + +// Errors: +// EBADF -- bad socket id +PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog) +{ +#if 0 + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + TBind bindReq; + PRNetAddr addr; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (backlog == 0) + backlog = 1; + + if (endpoint == NULL) { + err = EBADF; + goto ErrorExit; + } + + addr.inet.port = addr.inet.ip = 0; + + bindReq.addr.maxlen = PR_NETADDR_SIZE (&addr); + bindReq.addr.len = 0; + bindReq.addr.buf = (UInt8*) &addr; + bindReq.qlen = 0; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTGetProtAddress(endpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTUnbind(endpoint); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + bindReq.qlen = backlog; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTBind(endpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie == NULL); + + return kOTNoError; + +ErrorExit: + macsock_map_error(err); + return -1; +#endif + +#pragma unused (fd, backlog) + return kOTNoError; + +} + +// Errors: +// EBADF -- bad socket id +PRInt32 _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + TBind bindReq; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (addr == NULL) { + err = kEFAULTErr; + goto ErrorExit; + } + +#if !defined(_PR_INET6) + addr->inet.family = AF_INET; +#endif + + PR_ASSERT(PR_NETADDR_SIZE(addr) >= (*addrlen)); + + bindReq.addr.len = *addrlen; + bindReq.addr.maxlen = *addrlen; + bindReq.addr.buf = (UInt8*) addr; + bindReq.qlen = 0; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTGetProtAddress(endpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie == &bindReq); + + return kOTNoError; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) +{ + OSStatus err; + PRInt32 osfd = fd->secret->md.osfd; + EndpointRef endpoint = (EndpointRef) osfd; + TOptMgmt cmd; + TOption *opt; + PRThread *me = _PR_MD_CURRENT_THREAD(); + unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData)]; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + /* + OT wants IPPROTO_IP for level and not XTI_GENERIC. SO_REUSEADDR and SO_KEEPALIVE + are equated to IP level and TCP level options respectively and hence we need to set + the level correctly. + */ + if (level == SOL_SOCKET) { + if (optname == SO_REUSEADDR) + level = IPPROTO_IP; + else if (optname == SO_KEEPALIVE) + level = INET_TCP; + } + + opt = (TOption *)&optionBuffer[0]; + opt->len = sizeof(TOption); + opt->level = level; + opt->name = optname; + opt->status = 0; + + cmd.opt.len = sizeof(TOption); + cmd.opt.maxlen = sizeof(optionBuffer); + cmd.opt.buf = (UInt8*)optionBuffer; + cmd.flags = T_CURRENT; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTOptionManagement(endpoint, &cmd, &cmd); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){ + err = kEOPNOTSUPPErr; + goto ErrorExit; + } + + PR_ASSERT(opt->status == T_SUCCESS); + + switch (optname) { + case SO_LINGER: + *((t_linger*)optval) = *((t_linger*)&opt->value); + *optlen = sizeof(t_linger); + break; + case SO_REUSEADDR: + case TCP_NODELAY: + case SO_KEEPALIVE: + case SO_RCVBUF: + case SO_SNDBUF: + *((PRIntn*)optval) = *((PRIntn*)&opt->value); + *optlen = sizeof(PRIntn); + break; + case IP_MULTICAST_LOOP: + *((PRUint8*)optval) = *((PRIntn*)&opt->value); + *optlen = sizeof(PRUint8); + break; + case IP_TTL: + *((PRUintn*)optval) = *((PRUint8*)&opt->value); + *optlen = sizeof(PRUintn); + break; + case IP_MULTICAST_TTL: + *((PRUint8*)optval) = *((PRUint8*)&opt->value); + *optlen = sizeof(PRUint8); + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* struct ip_mreq and TIPAddMulticast are the same size and optval + is pointing to struct ip_mreq */ + *((struct ip_mreq *)optval) = *((struct ip_mreq *)&opt->value); + *optlen = sizeof(struct ip_mreq); + break; + } + case IP_MULTICAST_IF: + { + *((PRUint32*)optval) = *((PRUint32*)&opt->value); + *optlen = sizeof(PRUint32); + break; + } + /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */ + case TCP_MAXSEG: + if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */ + *((PRIntn*)optval) = *((PRIntn*)&opt->value); + *optlen = sizeof(PRIntn); + } else { /* it is IP_TOS */ + *((PRUintn*)optval) = *((PRUint8*)&opt->value); + *optlen = sizeof(PRUintn); + } + break; + default: + PR_ASSERT(0); + break; + } + + return PR_SUCCESS; + +ErrorExit: + macsock_map_error(err); + return PR_FAILURE; +} + +PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) +{ + OSStatus err; + PRInt32 osfd = fd->secret->md.osfd; + EndpointRef endpoint = (EndpointRef) osfd; + TOptMgmt cmd; + TOption *opt; + PRThread *me = _PR_MD_CURRENT_THREAD(); + unsigned char optionBuffer[kOTOptionHeaderSize + sizeof(PRSocketOptionData) + 1]; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + /* + OT wants IPPROTO_IP for level and not XTI_GENERIC. SO_REUSEADDR and SO_KEEPALIVE + are equated to IP level and TCP level options respectively and hence we need to set + the level correctly. + */ + if (level == SOL_SOCKET) { + if (optname == SO_REUSEADDR) + level = IPPROTO_IP; + else if (optname == SO_KEEPALIVE) + level = INET_TCP; + } + + opt = (TOption *)&optionBuffer[0]; + opt->len = kOTOptionHeaderSize + optlen; + + /* special case adjustments for length follow */ + if (optname == SO_KEEPALIVE) /* we need to pass the timeout value for OT */ + opt->len = kOTOptionHeaderSize + sizeof(t_kpalive); + if (optname == IP_MULTICAST_TTL || optname == IP_TTL) /* it is an unsigned char value */ + opt->len = kOTOneByteOptionSize; + if (optname == IP_TOS && level == IPPROTO_IP) + opt->len = kOTOneByteOptionSize; + + opt->level = level; + opt->name = optname; + opt->status = 0; + + cmd.opt.len = opt->len; + cmd.opt.maxlen = sizeof(optionBuffer); + cmd.opt.buf = (UInt8*)optionBuffer; + + optionBuffer[opt->len] = 0; + + cmd.flags = T_NEGOTIATE; + + switch (optname) { + case SO_LINGER: + *((t_linger*)&opt->value) = *((t_linger*)optval); + break; + case SO_REUSEADDR: + case TCP_NODELAY: + case SO_RCVBUF: + case SO_SNDBUF: + *((PRIntn*)&opt->value) = *((PRIntn*)optval); + break; + case IP_MULTICAST_LOOP: + if (*optval != 0) + opt->value[0] = T_YES; + else + opt->value[0] = T_NO; + break; + case SO_KEEPALIVE: + { + t_kpalive *kpalive = (t_kpalive *)&opt->value; + + kpalive->kp_onoff = *((long*)optval); + kpalive->kp_timeout = 10; /* timeout in minutes */ + break; + } + case IP_TTL: + *((unsigned char*)&opt->value) = *((PRUintn*)optval); + break; + case IP_MULTICAST_TTL: + *((unsigned char*)&opt->value) = *optval; + break; + case IP_ADD_MEMBERSHIP: + case IP_DROP_MEMBERSHIP: + { + /* struct ip_mreq and TIPAddMulticast are the same size and optval + is pointing to struct ip_mreq */ + *((TIPAddMulticast *)&opt->value) = *((TIPAddMulticast *)optval); + break; + } + case IP_MULTICAST_IF: + { + *((PRUint32*)&opt->value) = *((PRUint32*)optval); + break; + } + /*case IP_TOS:*/ /*IP_TOS has same value as TCP_MAXSEG */ + case TCP_MAXSEG: + if (level == IPPROTO_TCP) { /* it is TCP_MAXSEG */ + *((PRIntn*)&opt->value) = *((PRIntn*)optval); + } else { /* it is IP_TOS */ + *((unsigned char*)&opt->value) = *((PRUintn*)optval); + } + break; + default: + PR_ASSERT(0); + break; + } + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTOptionManagement(endpoint, &cmd, &cmd); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + if (opt->status == T_FAILURE || opt->status == T_NOTSUPPORT){ + err = kEOPNOTSUPPErr; + goto ErrorExit; + } + + if (level == IPPROTO_TCP && optname == TCP_MAXSEG && opt->status == T_READONLY) { + err = kEOPNOTSUPPErr; + goto ErrorExit; + } + + PR_ASSERT(opt->status == T_SUCCESS); + + return PR_SUCCESS; + +ErrorExit: + macsock_map_error(err); + return PR_FAILURE; +} + +PRInt32 _MD_socketavailable(PRFileDesc *fd) +{ + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + size_t bytes; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + bytes = 0; + + err = OTCountDataBytes(endpoint, &bytes); + if ((err == kOTLookErr) || // Not really errors, we just need to do a read, + (err == kOTNoDataErr)) // or thereÕs nothing there. + err = kOTNoError; + + if (err != kOTNoError) + goto ErrorExit; + + return bytes; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + TBind bindReq; + PRNetAddr bindAddr; + PRInt32 newosfd = -1; + EndpointRef newEndpoint; + TCall call; + PRNetAddr callAddr; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + memset(&call, 0 , sizeof(call)); + + call.addr.maxlen = PR_NETADDR_SIZE(&callAddr); + call.addr.len = PR_NETADDR_SIZE(&callAddr); + call.addr.buf = (UInt8*) &callAddr; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTListen (endpoint, &call); + if (err != kOTNoError && (err != kOTNoDataErr || fd->secret->nonblocking)) { + me->io_pending = PR_FALSE; + goto ErrorExit; + } + + while (err == kOTNoDataErr) { + WaitOnThisThread(me, timeout); + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTListen (endpoint, &call); + if (err == kOTNoError) + break; + + PR_ASSERT(err == kOTNoDataErr); + } + + newosfd = _MD_socket(AF_INET, SOCK_STREAM, 0); + if (newosfd == -1) + return -1; + + newEndpoint = (EndpointRef)newosfd; + + // Bind to a local port; let the system assign it. + + bindAddr.inet.port = bindAddr.inet.ip = 0; + + bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr); + bindReq.addr.len = 0; + bindReq.addr.buf = (UInt8*) &bindAddr; + bindReq.qlen = 0; + + PrepareThreadForAsyncIO(me, newEndpoint, newosfd); + + err = OTBind(newEndpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, timeout); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PrepareThreadForAsyncIO(me, endpoint, newosfd); + + err = OTAccept (endpoint, newEndpoint, &call); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, timeout); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie != NULL); + + if (addr != NULL) + *addr = callAddr; + if (addrlen != NULL) + *addrlen = call.addr.len; + + return newosfd; + +ErrorExit: + if (newosfd != -1) + _MD_closesocket(newosfd); + macsock_map_error(err); + return -1; +} + +PRInt32 _MD_connect(PRFileDesc *fd, PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + TCall sndCall; + TBind bindReq; + PRNetAddr bindAddr; + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (addr == NULL) { + err = kEFAULTErr; + goto ErrorExit; + } + + // Bind to a local port; let the system assign it. + + bindAddr.inet.port = bindAddr.inet.ip = 0; + + bindReq.addr.maxlen = PR_NETADDR_SIZE (&bindAddr); + bindReq.addr.len = 0; + bindReq.addr.buf = (UInt8*) &bindAddr; + bindReq.qlen = 0; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTBind(endpoint, &bindReq, NULL); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + memset(&sndCall, 0 , sizeof(sndCall)); + + sndCall.addr.maxlen = addrlen; + sndCall.addr.len = addrlen; + sndCall.addr.buf = (UInt8*) addr; + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + err = OTConnect (endpoint, &sndCall, NULL); + if (err != kOTNoError && err != kOTNoDataErr) + goto ErrorExit; + if (err == kOTNoDataErr && fd->secret->nonblocking) { + err = kEINPROGRESSErr; + me->io_pending = PR_FALSE; + goto ErrorExit; + } + + WaitOnThisThread(me, timeout); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie != NULL); + + err = OTRcvConnect(endpoint, NULL); + PR_ASSERT(err == kOTNoError); + + return kOTNoError; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +// Errors: +// EBADF -- bad socket id +// EFAULT -- bad buffer +static PRInt32 SendReceiveStream(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout, SndRcvOpCode opCode) +{ + OSStatus err; + OTResult result; + PRInt32 osfd = fd->secret->md.osfd; + EndpointRef endpoint = (EndpointRef) osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 bytesLeft = amount; + + PR_ASSERT(flags == 0); + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (buf == NULL) { + err = kEFAULTErr; + goto ErrorExit; + } + + while (bytesLeft > 0) { + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + if (opCode == kSTREAM_SEND) + result = OTSnd(endpoint, buf, bytesLeft, NULL); + else if (opCode == kSTREAM_RECEIVE) + result = OTRcv(endpoint, buf, bytesLeft, NULL); + else { + err = kEINVALErr; + goto ErrorExit; + } + + if (result > 0) { + buf = (void *) ( (UInt32) buf + (UInt32)result ); + bytesLeft -= result; + me->io_pending = PR_FALSE; + if (opCode == kSTREAM_RECEIVE) + return result; + } else { + if (result == kOTOutStateErr) { /* it has been closed */ + return 0; + } + if (result == kOTLookErr) { + PRBool readReady,writeReady,exceptReady; + /* process the event and then continue the operation */ + (void) GetState(endpoint, &readReady, &writeReady, &exceptReady); + continue; + } + if (result != kOTNoDataErr && result != kOTFlowErr && + result != kEAGAINErr && result != kEWOULDBLOCKErr) { + err = result; + goto ErrorExit; + } else if (fd->secret->nonblocking) { + me->io_pending = PR_FALSE; + err = result; + goto ErrorExit; + } + WaitOnThisThread(me, timeout); + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie != NULL); + } + } + + return amount; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + return (SendReceiveStream(fd, buf, amount, flags, timeout, kSTREAM_RECEIVE)); +} + +PRInt32 _MD_send(PRFileDesc *fd,const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + return (SendReceiveStream(fd, (void *)buf, amount, flags, timeout, kSTREAM_SEND)); +} + + +// Errors: +// EBADF -- bad socket id +// EFAULT -- bad buffer +static PRInt32 SendReceiveDgram(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout, SndRcvOpCode opCode) +{ + OSStatus err; + PRInt32 osfd = fd->secret->md.osfd; + EndpointRef endpoint = (EndpointRef) osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 bytesLeft = amount; + TUnitData dgram; + + PR_ASSERT(flags == 0); + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (buf == NULL || addr == NULL) { + err = kEFAULTErr; + goto ErrorExit; + } + + memset(&dgram, 0 , sizeof(dgram)); + dgram.addr.maxlen = *addrlen; + dgram.addr.len = *addrlen; + dgram.addr.buf = (UInt8*) addr; + dgram.udata.maxlen = amount; + dgram.udata.len = amount; + dgram.udata.buf = (UInt8*) buf; + + while (bytesLeft > 0) { + + PrepareThreadForAsyncIO(me, endpoint, osfd); + + if (opCode == kDGRAM_SEND) + err = OTSndUData(endpoint, &dgram); + else if (opCode == kDGRAM_RECEIVE) + err = OTRcvUData(endpoint, &dgram, NULL); + else { + err = kEINVALErr; + goto ErrorExit; + } + + if (err == kOTNoError) { + buf = (void *) ( (UInt32) buf + (UInt32)dgram.udata.len ); + bytesLeft -= dgram.udata.len; + dgram.udata.buf = (UInt8*) buf; + me->io_pending = PR_FALSE; + } + else { + PR_ASSERT(err == kOTNoDataErr || err == kOTOutStateErr); + WaitOnThisThread(me, timeout); + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + PR_ASSERT(me->md.cookie != NULL); + } + } + + if (opCode == kDGRAM_RECEIVE) + *addrlen = dgram.addr.len; + + return amount; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +PRInt32 _MD_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 *addrlen, + PRIntervalTime timeout) +{ + return (SendReceiveDgram(fd, buf, amount, flags, addr, addrlen, + timeout, kDGRAM_RECEIVE)); +} + +PRInt32 _MD_sendto(PRFileDesc *fd,const void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRUint32 addrlen, + PRIntervalTime timeout) +{ + return (SendReceiveDgram(fd, (void *)buf, amount, flags, addr, &addrlen, + timeout, kDGRAM_SEND)); +} + +PRInt32 _MD_closesocket(PRInt32 osfd) +{ + OSStatus err; + EndpointRef endpoint = (EndpointRef) osfd; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (endpoint == NULL) { + err = kEBADFErr; + goto ErrorExit; + } + + if (me->io_pending && me->io_fd == osfd) + me->io_pending = PR_FALSE; + +#if 0 + { + OTResult state; + state = OTGetEndpointState(endpoint); + + err = OTSndOrderlyDisconnect(endpoint); + if (err != kOTNoError && err != kOTOutStateErr) + goto ErrorExit; + + state = OTGetEndpointState(endpoint); + + err = OTUnbind(endpoint); + if (err != kOTNoError && err != kOTOutStateErr) + goto ErrorExit; + + state = OTGetEndpointState(endpoint); + + err = OTSetSynchronous(endpoint); + if (err != kOTNoError) + goto ErrorExit; + + err = OTSetBlocking(endpoint); + if (err != kOTNoError) + goto ErrorExit; + } +#endif + + (void) OTSndOrderlyDisconnect(endpoint); + + err = OTCloseProvider(endpoint); + if (err != kOTNoError) + goto ErrorExit; + + return kOTNoError; + +ErrorExit: + macsock_map_error(err); + return -1; +} + +PRInt32 _MD_writev(PRFileDesc *fd, struct PRIOVec *iov, PRInt32 iov_size, PRIntervalTime timeout) +{ +#pragma unused (fd, iov, iov_size, timeout) + + PR_ASSERT(0); + _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr; + return -1; +} + +static PRBool GetState(EndpointRef endpoint, PRBool *readReady, PRBool *writeReady, PRBool *exceptReady) +{ + OSStatus err; + OTResult resultOT; + TDiscon discon; + PRBool result = PR_FALSE; + + *readReady = *writeReady = *exceptReady = PR_FALSE; + + resultOT = OTLook(endpoint); + switch (resultOT) { + case T_DATA: + case T_LISTEN: + *readReady = PR_TRUE; + break; + case T_CONNECT: + err = OTRcvConnect(endpoint, NULL); + PR_ASSERT(err == kOTNoError); + break; + case T_DISCONNECT: + memset(&discon, 0 , sizeof(discon)); + err = OTRcvDisconnect(endpoint, &discon); + PR_ASSERT(err == kOTNoError); + macsock_map_error(discon.reason); + *exceptReady = PR_TRUE; + break; + case T_ORDREL: + *readReady = PR_TRUE; + err = OTRcvOrderlyDisconnect(endpoint); + PR_ASSERT(err == kOTNoError); + break; + } + resultOT = OTGetEndpointState(endpoint); + switch (resultOT) { + case T_DATAXFER: + case T_INREL: + *writeReady = PR_TRUE; + break; + default: + *writeReady = PR_FALSE; + } + + if ((*readReady == PR_TRUE) || (*writeReady==PR_TRUE) || (*exceptReady==PR_TRUE)) + result = PR_TRUE; + + return result; +} + +PRInt32 _MD_poll(PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRInt32 n = 0; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime sleepTime; + PRIntervalTime timein = PR_IntervalNow(); + + sleepTime = PR_MillisecondsToInterval(5UL); + if (sleepTime > timeout) + sleepTime = timeout; + + do { + + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRInt32 osfd; + PRInt16 in_flags = pd->in_flags; + PRFileDesc *bottom = pd->fd; + EndpointRef endpoint; + PRInt16 out_flags = 0; + PRBool readReady, writeReady, exceptReady; + + pd->out_flags = 0; + if (NULL == bottom || in_flags == 0) { + continue; + } + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + osfd = bottom->secret->md.osfd; + endpoint = (EndpointRef) osfd; + + if (GetState(endpoint, &readReady, &writeReady, &exceptReady)) { + + if ((in_flags & PR_POLL_READ) && (readReady)) { + out_flags |= PR_POLL_READ; + } + if ((in_flags & PR_POLL_WRITE) && (writeReady)) { + out_flags |= PR_POLL_WRITE; + } + if ((in_flags & PR_POLL_EXCEPT) && (exceptReady)) { + out_flags |= PR_POLL_EXCEPT; + } + pd->out_flags = out_flags; + if (out_flags) { + n++; + } + } + } + + if (n > 0) + return n; + + (void) PR_Sleep(sleepTime); + + } while ((timeout == PR_INTERVAL_NO_TIMEOUT) || + (((PRIntervalTime)(PR_IntervalNow() - timein)) < timeout)); + + return 0; /* timed out */ +} + +void _MD_makenonblock(PRFileDesc *fd) +{ + OSStatus err; + PRInt32 osfd = fd->secret->md.osfd; + EndpointRef endpoint = (EndpointRef) osfd; + + err = OTSetNonBlocking(endpoint); + PR_ASSERT(err == kOTNoError || err == kOTOutStateErr); +} + +PR_IMPLEMENT(PRInt32) _MD_shutdown(PRFileDesc *fd, PRIntn how) +{ +#pragma unused (fd, how) + +/* Just succeed silently!!! */ +return (0); +} + +PR_IMPLEMENT(PRStatus) _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen) +{ +#pragma unused (fd, addr, addrlen) + + PR_ASSERT(0); + _PR_MD_CURRENT_THREAD()->md.osErrCode = unimpErr; + return PR_FAILURE; +} + + +PR_IMPLEMENT(unsigned long) inet_addr(const char *cp) +{ + OSStatus err; + InetHost host; + + err = OTInetStringToHost((char*) cp, &host); + PR_ASSERT(err == kOTNoError); + + return host; +} + + +static char *sAliases[1] = {NULL}; +static struct hostent sHostEnt = {NULL, &sAliases[0], AF_INET, sizeof (long), NULL}; +static InetHostInfo sHostInfo; +static InetHost *sAddresses[kMaxHostAddrs+1]; + +PR_IMPLEMENT(struct hostent *) gethostbyname(const char * name) +{ + OSStatus err; + PRUint32 index; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PrepareThreadForAsyncIO(me, sSvcRef, NULL); + + err = OTInetStringToAddress(sSvcRef, (char *)name, &sHostInfo); + if (err != kOTNoError) + goto ErrorExit; + + WaitOnThisThread(me, PR_INTERVAL_NO_TIMEOUT); + + err = me->md.osErrCode; + if (err != kOTNoError) + goto ErrorExit; + + sHostEnt.h_name = sHostInfo.name; + for (index=0; index<kMaxHostAddrs && sHostInfo.addrs[index] != NULL; index++) + sAddresses[index] = &sHostInfo.addrs[index]; + sAddresses[index] = NULL; + sHostEnt.h_addr_list = (char **)sAddresses; + + return (&sHostEnt); + +ErrorExit: + macsock_map_error(err); + return NULL; +} + + +PR_IMPLEMENT(struct hostent *) gethostbyaddr(const void *addr, int addrlen, int type) +{ + PR_ASSERT(type == AF_INET); + PR_ASSERT(addrlen == sizeof(struct in_addr)); + + OTInetHostToString((InetHost)addr, sHostInfo.name); + + return (gethostbyname(sHostInfo.name)); +} + + +PR_IMPLEMENT(char *) inet_ntoa(struct in_addr addr) +{ + OTInetHostToString((InetHost)addr.s_addr, sHostInfo.name); + + return sHostInfo.name; +} + + +PR_IMPLEMENT(PRStatus) _MD_gethostname(char *name, int namelen) +{ + OSStatus err; + InetInterfaceInfo info; + + /* + * On a Macintosh, we donÕt have the concept of a local host name. + * We do though have an IP address & everyone should be happy with + * a string version of that for a name. + * The alternative here is to ping a local DNS for our name, they + * will often know it. This is the cheap, easiest, and safest way out. + */ + + /* Make sure the string is as long as the longest possible address */ + if (namelen < strlen("123.123.123.123")) { + err = kEINVALErr; + goto ErrorExit; + } + + err = OTInetGetInterfaceInfo(&info, kDefaultInetInterface); + if (err != kOTNoError) + goto ErrorExit; + + OTInetHostToString(info.fAddress, name); + + return PR_SUCCESS; + +ErrorExit: + macsock_map_error(err); + return PR_FAILURE; +} + + +#define kIPName "ip" +static struct protoent sIPProto = {kIPName, NULL, INET_IP}; +static struct protoent sTCPProto = {kTCPName, NULL, INET_TCP}; +static struct protoent sUDPProto = {kUDPName, NULL, INET_UDP}; + +PR_IMPLEMENT(struct protoent *) getprotobyname(const char * name) +{ + if (strcmp(name, kIPName) == 0) + return (&sIPProto); + + if (strcmp(name, kTCPName) == 0) + return (&sTCPProto); + + if (strcmp(name, kUDPName) == 0) + return (&sUDPProto); + +ErrorExit: + macsock_map_error(kEINVALErr); + return NULL; +} + + +PR_IMPLEMENT(struct protoent *) getprotobynumber(int number) +{ + if (number == INET_IP) + return (&sIPProto); + + if (number == INET_TCP) + return (&sTCPProto); + + if (number == INET_UDP) + return (&sUDPProto); + +ErrorExit: + macsock_map_error(kEINVALErr); + return NULL; +} + + +int _MD_mac_get_nonblocking_connect_error(PRInt32 osfd) +{ + OTResult resultOT; + EndpointRef endpoint = (EndpointRef) osfd; + + resultOT = OTGetEndpointState(endpoint); + switch (resultOT) { + case T_OUTCON: + macsock_map_error(kEINPROGRESSErr); + return -1; + case T_DATAXFER: + return 0; + case T_IDLE: + return -1; + default: + PR_ASSERT(0); + return -1; + } +} diff --git a/pr/src/md/mac/macthr.c b/pr/src/md/mac/macthr.c new file mode 100644 index 00000000..fa5a4637 --- /dev/null +++ b/pr/src/md/mac/macthr.c @@ -0,0 +1,381 @@ +/* -*- 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 <Types.h> +#include <Timer.h> +#include <OSUtils.h> + +#include <LowMem.h> + +// This should be in LowMem.h +#ifndef LMSetStackLowPoint +#define LMSetStackLowPoint(value) \ + *((UInt32 *)(0x110)) = (value) +#endif + +PRThread *gPrimaryThread = NULL; + +PR_IMPLEMENT(PRThread *) PR_GetPrimaryThread() +{ + return gPrimaryThread; +} + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark CREATING MACINTOSH THREAD STACKS + + +/* +** Allocate a new memory segment. We allocate it from our figment heap. Currently, +** it is being used for per thread stack space. +** +** Return the segment's access rights and size. vaddr is used on Unix platforms to +** map an existing address for the segment. +*/ +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) { + +#if DEBUG + DebugStr("\p_MD_AllocSegment failed."); +#endif + + return PR_FAILURE; + } + + seg->access = PR_SEGMENT_RDWR; + seg->size = size; + + return PR_SUCCESS; +} + + +/* +** Free previously allocated memory segment. +*/ +void _MD_FreeSegment(PRSegment *seg) +{ + PR_ASSERT((seg->flags & _PR_SEG_VM) == 0); + + if (seg->vaddr != NULL) + DisposePtr((Ptr)(seg->vaddr)); +} + + +/* +** The thread's stack has been allocated and its fields are already properly filled +** in by PR. Perform any debugging related initialization here. +** +** Put a recognizable pattern so that we can find it from Macsbug. +** Put a cookie at the top of the stack so that we can find it from Macsbug. +*/ +void _MD_InitStack(PRThreadStack *ts, int redZoneBytes) + { +#pragma unused (redZoneBytes) +#if DEVELOPER_DEBUG + // Put a cookie at the top of the stack so that we can find + // it from Macsbug. + + memset(ts->allocBase, 0xDC, ts->stackSize); + + ((UInt32 *)ts->stackTop)[-1] = 0xBEEFCAFE; + ((UInt32 *)ts->stackTop)[-2] = (UInt32)gPrimaryThread; + ((UInt32 *)ts->stackTop)[-3] = (UInt32)(ts); + ((UInt32 *)ts->stackBottom)[0] = 0xCAFEBEEF; +#else +#pragma unused (ts) +#endif + + /* + ** Turn off the snack stiffer. The NSPR stacks are allocated in the + ** application's heap; this throws the stack sniffer for a tizzy. + ** Note that the sniffer does not run on machines running the thread manager. + ** Yes, we will blast the low-mem every time a new stack is created. We can afford + ** a couple extra cycles. + */ + LMSetStackLowPoint(0); + } + +extern void _MD_ClearStack(PRThreadStack *ts) + { +#if DEVELOPER_DEBUG + // Clear out our cookies. + + memset(ts->allocBase, 0xEF, ts->allocSize); + ((UInt32 *)ts->stackTop)[-1] = 0; + ((UInt32 *)ts->stackTop)[-2] = 0; + ((UInt32 *)ts->stackTop)[-3] = 0; + ((UInt32 *)ts->stackBottom)[0] = 0; +#else +#pragma unused (ts) +#endif + } + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark TIME MANAGER-BASED CLOCK + +TMTask gTimeManagerTaskElem; + +extern void _MD_IOInterrupt(void); +_PRInterruptTable _pr_interruptTable[] = { + { "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, }, + { "i/o", _PR_MISSED_IO, _MD_IOInterrupt, }, + { 0 } +}; + +pascal void TimerCallback(TMTaskPtr tmTaskPtr) +{ + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + if (_PR_MD_GET_INTSOFF()) { + cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK; + PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs); + return; + } + _PR_SET_INTSOFF(1); + + // And tell nspr that a clock interrupt occured. + _PR_ClockInterrupt(); + + if ((_PR_RUNQREADYMASK(cpu)) >> ((_PR_MD_CURRENT_THREAD()->priority))) + _PR_SET_RESCHED_FLAG(); + + _PR_SET_INTSOFF(0); + + // Reset the clock timer so that we fire again. + PrimeTime((QElemPtr)tmTaskPtr, kMacTimerInMiliSecs); +} + +#if GENERATINGCFM + +RoutineDescriptor gTimerCallbackRD = BUILD_ROUTINE_DESCRIPTOR(uppTimerProcInfo, &TimerCallback); + +#else + +asm void gTimerCallbackRD(void) +{ + // Check out LocalA5. If it is zero, then + // it is our first time through, and we should + // store away our A5. If not, then we are + // a real time manager callback, so we should + // store away A5, set up our local a5, jsr + // to our callback, and then restore the + // previous A5. + + lea LocalA5, a0 + move.l (a0), d0 + cmpi.l #0, d0 + bne TimerProc + + move.l a5, (a0) + rts + +TimerProc: + + // Save A5, restore our local A5 + + move.l a5, -(sp) + move.l d0, a5 + + // Jump to our C routine + + move.l a1, -(sp) + jsr TimerCallback + + // Restore the previous A5 + + move.l (sp)+, a5 + + rts + +LocalA5: + + dc.l 0 + +} + +#endif + +void _MD_StartInterrupts(void) +{ + gPrimaryThread = _PR_MD_CURRENT_THREAD(); + + // If we are not generating CFM-happy code, then make sure that + // we call our callback wrapper once so that we can + // save away our A5. + +#if !GENERATINGCFM + gTimerCallbackRD(); +#endif + + // Fill in the Time Manager queue element + + gTimeManagerTaskElem.tmAddr = (TimerUPP)&gTimerCallbackRD; + gTimeManagerTaskElem.tmCount = 0; + gTimeManagerTaskElem.tmWakeUp = 0; + gTimeManagerTaskElem.tmReserved = 0; + + // Make sure that our time manager task is ready to go. + InsTime((QElemPtr)&gTimeManagerTaskElem); + + PrimeTime((QElemPtr)&gTimeManagerTaskElem, kMacTimerInMiliSecs); +} + +void _MD_StopInterrupts(void) +{ + if (gTimeManagerTaskElem.tmAddr != NULL) { + RmvTime((QElemPtr)&gTimeManagerTaskElem); + gTimeManagerTaskElem.tmAddr = NULL; + } +} + +void _MD_PauseCPU(PRIntervalTime timeout) +{ +#pragma unused (timeout) + + unsigned long finalTicks; + + if (timeout != PR_INTERVAL_NO_WAIT) { + Delay(1,&finalTicks); + (void) _MD_IOInterrupt(); + } +} + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark THREAD SUPPORT FUNCTIONS + +#include <OpenTransport.h> /* for error codes */ + +PRStatus _MD_InitThread(PRThread *thread) +{ + thread->md.asyncIOLock = PR_NewLock(); + PR_ASSERT(thread->md.asyncIOLock != NULL); + thread->md.asyncIOCVar = PR_NewCondVar(thread->md.asyncIOLock); + PR_ASSERT(thread->md.asyncIOCVar != NULL); + + if (thread->md.asyncIOLock == NULL || thread->md.asyncIOCVar == NULL) + return PR_FAILURE; + else + return PR_SUCCESS; +} + +PRStatus _MD_wait(PRThread *thread, PRIntervalTime timeout) +{ +#pragma unused (timeout) + + _MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +void WaitOnThisThread(PRThread *thread, PRIntervalTime timeout) +{ + intn is; + PRIntervalTime timein = PR_IntervalNow(); + PRStatus status = PR_SUCCESS; + + _PR_INTSOFF(is); + PR_Lock(thread->md.asyncIOLock); + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + while ((thread->io_pending) && (status == PR_SUCCESS)) + status = PR_WaitCondVar(thread->md.asyncIOCVar, PR_INTERVAL_NO_TIMEOUT); + } else { + while ((thread->io_pending) && ((PRIntervalTime)(PR_IntervalNow() - timein) < timeout)) + status = PR_WaitCondVar(thread->md.asyncIOCVar, timeout); + } + if ((status == PR_FAILURE) && (PR_GetError() == PR_PENDING_INTERRUPT_ERROR)) { + thread->md.osErrCode = kEINTRErr; + } else if (thread->io_pending) { + thread->md.osErrCode = kETIMEDOUTErr; + PR_SetError(PR_IO_TIMEOUT_ERROR, kETIMEDOUTErr); + } + PR_Unlock(thread->md.asyncIOLock); + _PR_FAST_INTSON(is); +} + +void DoneWaitingOnThisThread(PRThread *thread) +{ + intn is; + + _PR_INTSOFF(is); + PR_Lock(thread->md.asyncIOLock); + thread->io_pending = PR_FALSE; + /* let the waiting thread know that async IO completed */ + PR_NotifyCondVar(thread->md.asyncIOCVar); + PR_Unlock(thread->md.asyncIOLock); + _PR_FAST_INTSON(is); +} + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark PROCESS SUPPORT FUNCTIONS + +PRProcess * _MD_CreateProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ +#pragma unused (path, argv, envp, attr) + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); + return NULL; +} + +PRStatus _MD_DetachProcess(PRProcess *process) +{ +#pragma unused (process) + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); + return PR_FAILURE; +} + +PRStatus _MD_WaitProcess(PRProcess *process, PRInt32 *exitCode) +{ +#pragma unused (process, exitCode) + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); + return PR_FAILURE; +} + +PRStatus _MD_KillProcess(PRProcess *process) +{ +#pragma unused (process) + + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, unimpErr); + return PR_FAILURE; +} diff --git a/pr/src/md/mac/mactime.c b/pr/src/md/mac/mactime.c new file mode 100644 index 00000000..254e7b47 --- /dev/null +++ b/pr/src/md/mac/mactime.c @@ -0,0 +1,266 @@ +/* -*- 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 <OSUtils.h> +#include <time.h> + +#include "primpl.h" + +PR_IMPLEMENT(UnsignedWide) dstLocalBaseMicroseconds; +PR_IMPLEMENT(unsigned long) gJanuaryFirst1970Seconds; + +/* + * The geographic location and time zone information of a Mac + * are stored in extended parameter RAM. The ReadLocation + * produdure uses the geographic location record, MachineLocation, + * to read the geographic location and time zone information in + * extended parameter RAM. + * + * Because serial port and SLIP conflict with ReadXPram calls, + * we cache the call here. + * + * Caveat: this caching will give the wrong result if a session + * extend across the DST changeover time. + */ + +static void MyReadLocation(MachineLocation *loc) +{ + static MachineLocation storedLoc; + static Boolean didReadLocation = false; + + if (!didReadLocation) { + ReadLocation(&storedLoc); + didReadLocation = true; + } + *loc = storedLoc; +} + +static long GMTDelta(void) +{ + MachineLocation loc; + long gmtDelta; + + MyReadLocation(&loc); + gmtDelta = loc.u.gmtDelta & 0x00ffffff; + if (gmtDelta & 0x00800000) { /* test sign extend bit */ + gmtDelta |= 0xff000000; + } + return gmtDelta; +} + +void MacintoshInitializeTime(void) +{ + /* + * The NSPR epoch is midnight, Jan. 1, 1970 GMT. + * + * At midnight Jan. 1, 1970 GMT, the local time was + * midnight Jan. 1, 1970 + GMTDelta(). + * + * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17) + * = 2082844800 seconds since the Mac epoch. + * (There were 17 leap years from 1904 to 1970.) + * + * So the NSPR epoch is 2082844800 + GMTDelta() seconds since + * the Mac epoch. Whew! :-) + */ + gJanuaryFirst1970Seconds = 2082844800 + GMTDelta(); + + /* + * Set up dstLocalBaseMicroseconds just for "prmjtime.c" for Mocha + * needs. The entire MOcha time needs to be rewritten using NSPR 2.0 + * time. + */ + { + UnsignedWide upTime; + unsigned long currentLocalTimeSeconds, + startupTimeSeconds; + uint64 startupTimeMicroSeconds; + uint32 upTimeSeconds; + uint64 oneMillion, upTimeSecondsLong, microSecondsToSeconds; + + Microseconds(&upTime); + + GetDateTime(¤tLocalTimeSeconds); + + LL_I2L(microSecondsToSeconds, PR_USEC_PER_SEC); + LL_DIV(upTimeSecondsLong, *((uint64 *)&upTime), microSecondsToSeconds); + LL_L2I(upTimeSeconds, upTimeSecondsLong); + + startupTimeSeconds = currentLocalTimeSeconds - upTimeSeconds; + + startupTimeSeconds -= gJanuaryFirst1970Seconds; + + // Now convert the startup time into a wide so that we + // can figure out GMT and DST. + + LL_I2L(startupTimeMicroSeconds, startupTimeSeconds); + LL_I2L(oneMillion, PR_USEC_PER_SEC); + LL_MUL(dstLocalBaseMicroseconds, oneMillion, startupTimeMicroSeconds); + } +} + +/* + *----------------------------------------------------------------------- + * + * 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 Mac + * Implementation. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PRTime PR_Now(void) +{ + unsigned long currentTime; /* unsigned 32-bit integer, ranging + from midnight Jan. 1, 1904 to + 6:28:15 AM Feb. 6, 2040 */ + PRTime retVal; + int64 usecPerSec; + + /* + * Get the current time expressed as the number of seconds + * elapsed since the Mac epoch, midnight, Jan. 1, 1904 (local time). + * On a Mac, current time accuracy is up to a second. + */ + GetDateTime(¤tTime); + + /* + * Express the current time relative to the NSPR epoch, + * midnight, Jan. 1, 1970 GMT. + * + * At midnight Jan. 1, 1970 GMT, the local time was + * midnight Jan. 1, 1970 + GMTDelta(). + * + * Midnight Jan. 1, 1970 is 86400 * (365 * (1970 - 1904) + 17) + * = 2082844800 seconds since the Mac epoch. + * (There were 17 leap years from 1904 to 1970.) + * + * So the NSPR epoch is 2082844800 + GMTDelta() seconds since + * the Mac epoch. Whew! :-) + */ + currentTime = currentTime - 2082844800 - GMTDelta(); + + /* Convert seconds to microseconds */ + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(retVal, currentTime); + LL_MUL(retVal, retVal, usecPerSec); + + return retVal; +} + +/* + *------------------------------------------------------------------------- + * + * PR_LocalTimeParameters -- + * + * returns the time parameters for the local time zone + * + * This is the machine-dependent implementation for Mac. + * + * Caveat: On a Mac, we only know the GMT and DST offsets for + * the current time, not for the time in question. + * Mac has no support for DST handling. + * DST changeover is all manually set by the user. + * + *------------------------------------------------------------------------- + */ + +PRTimeParameters PR_LocalTimeParameters(const PRExplodedTime *gmt) +{ +#pragma unused (gmt) + + PRTimeParameters retVal; + MachineLocation loc; + + MyReadLocation(&loc); + + /* + * On a Mac, the GMT value is in seconds east of GMT. For example, + * San Francisco is at -28,800 seconds (8 hours * 3600 seconds per hour) + * east of GMT. The gmtDelta field is a 3-byte value contained in a + * long word, so you must take care to get it properly. + */ + + retVal.tp_gmt_offset = loc.u.gmtDelta & 0x00ffffff; + if (retVal.tp_gmt_offset & 0x00800000) { /* test sign extend bit */ + retVal.tp_gmt_offset |= 0xff000000; + } + + /* + * The daylight saving time value, dlsDelta, is a signed byte + * value representing the offset for the hour field -- whether + * to add 1 hour, subtract 1 hour, or make no change at all. + */ + + if (loc.u.dlsDelta) { + retVal.tp_gmt_offset -= 3600; + retVal.tp_dst_offset = 3600; + } else { + retVal.tp_dst_offset = 0; + } + return retVal; +} + +PRIntervalTime _MD_GetInterval(void) +{ + PRIntervalTime retVal; + PRUint64 upTime, micrototimer; + + /* + * Use the Microseconds procedure to obtain the number of + * microseconds elapsed since system startup time. + */ + Microseconds((UnsignedWide *)&upTime); + LL_I2L(micrototimer, (kMacTimerInMiliSecs * PR_MSEC_PER_SEC)); + LL_DIV(upTime, upTime, micrototimer); + LL_L2I(retVal, upTime); + + return retVal; +} + +struct tm *Maclocaltime(const time_t * t) +{ + DateTimeRec dtr; + MachineLocation loc; + time_t macLocal = *t + gJanuaryFirst1970Seconds; /* GMT Mac */ + static struct tm statictime; + static const short monthday[12] = + {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + + SecondsToDate(macLocal, &dtr); + statictime.tm_sec = dtr.second; + statictime.tm_min = dtr.minute; + statictime.tm_hour = dtr.hour; + statictime.tm_mday = dtr.day; + statictime.tm_mon = dtr.month - 1; + statictime.tm_year = dtr.year - 1900; + statictime.tm_wday = dtr.dayOfWeek - 1; + statictime.tm_yday = monthday[statictime.tm_mon] + + statictime.tm_mday - 1; + if (2 < statictime.tm_mon && !(statictime.tm_year & 3)) + ++statictime.tm_yday; + MyReadLocation(&loc); + statictime.tm_isdst = loc.u.dlsDelta; + return(&statictime); +} + + diff --git a/pr/src/md/mac/mdmac.c b/pr/src/md/mac/mdmac.c new file mode 100644 index 00000000..510d9b17 --- /dev/null +++ b/pr/src/md/mac/mdmac.c @@ -0,0 +1,842 @@ +/* -*- 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 <Types.h> +#include <Timer.h> +#include <Files.h> +#include <Errors.h> +#include <Folders.h> +#include <Events.h> +#include <Processes.h> +#include <TextUtils.h> +#include <MixedMode.h> +#include <LowMem.h> + +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <stat.h> +#include <stdarg.h> +#include <unix.h> + +#include "MacErrorHandling.h" + +#include "primpl.h" +#include "prgc.h" +#include "MacMemAllocator.h" + +enum { + kGarbageCollectionEmergencyFundSize = 16 * 1024, + kGarbageCollectionMemTightFundSize = 48 * 1024 +}; + +enum { + uppExitToShellProcInfo = kPascalStackBased, + uppStackSpaceProcInfo = kRegisterBased + | RESULT_SIZE(SIZE_CODE(sizeof(long))) + | REGISTER_RESULT_LOCATION(kRegisterD0) + | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(UInt16))) +}; + + +#define UNIMPLEMENTED_ROUTINE \ + DebugStr("\pNot Implemented Yet"); \ + return 0; + +// +// Local routines +// +void PStrFromCStr(const char *, Str255); +unsigned char GarbageCollectorCacheFlusher(PRUint32 size); + +extern PRThread *gPrimaryThread; + +pascal void ExitToShellPatch(void); + +UniversalProcPtr gExitToShellPatchCallThru = NULL; +#if GENERATINGCFM +RoutineDescriptor gExitToShellPatchRD = BUILD_ROUTINE_DESCRIPTOR(uppExitToShellProcInfo, &ExitToShellPatch); +#else +#define gExitToShellPatchRD ExitToShellPatch +#endif + +UniversalProcPtr gStackSpacePatchCallThru = NULL; +#if GENERATINGCFM +pascal long StackSpacePatch(UInt16); +RoutineDescriptor StackSpacePatchRD = BUILD_ROUTINE_DESCRIPTOR(uppStackSpaceProcInfo, &StackSpacePatch); +#else +pascal long StackSpacePatch(); +asm pascal long StackSpacePatchGlue(); +#endif + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark CREATING MACINTOSH THREAD STACKS + + +#if !GENERATINGCFM + +asm long pascal StackSpacePatchCallThruGlue(long theAddress) +{ + move.l 4(sp), a0 + jsr (a0) + move.l (sp)+, a0 + add #0x8, sp + move.l d0, -(sp) + jmp (a0) +} + +asm pascal long StackSpacePatchGlue() +{ + + // Check out LocalA5. If it is zero, then + // it is our first time through, and we should + // store away our A5. If not, then we are + // a real time manager callback, so we should + // store away A5, set up our local a5, jsr + // to our callback, and then restore the + // previous A5. + + lea LocalA5, a0 + move.l (a0), d0 + cmpi.l #0, d0 + bne DoStackSpace + + move.l a5, (a0) + rts + +DoStackSpace: + + // Save A5, restore our local A5 + + move.l a5, -(sp) + move.l d0, a5 + + // Jump to our C routine + + clr.l -(sp) + jsr StackSpacePatch + move.l (sp)+, d0 + + // Restore the previous A5 + + move.l (sp)+, a5 + + rts + +LocalA5: + + dc.l 0 + +} + +#endif + +#if GENERATINGCFM +pascal long StackSpacePatch(UInt16 trapNo) +#else +pascal long StackSpacePatch() +#endif +{ + char tos; + PRThread *thisThread; + + thisThread = PR_CurrentThread(); + + // If we are the primary thread, then call through to the + // good ol' fashion stack space implementation. Otherwise, + // compute it by hand. + if ((thisThread == gPrimaryThread) || + (&tos < thisThread->stack->stackBottom) || + (&tos > thisThread->stack->stackTop)) { +#if GENERATINGCFM + return CallOSTrapUniversalProc(gStackSpacePatchCallThru, uppStackSpaceProcInfo, trapNo); +#else + return StackSpacePatchCallThruGlue((long)gStackSpacePatchCallThru); +#endif + } + else { + return &tos - thisThread->stack->stackBottom; + } +} + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark ENVIRONMENT VARIABLES + + +typedef struct EnvVariable EnvVariable; + +struct EnvVariable { + char *variable; + char *value; + EnvVariable *next; +}; + +EnvVariable *gEnvironmentVariables = NULL; + +char *_MD_GetEnv(const char *name) +{ + EnvVariable *currentVariable = gEnvironmentVariables; + + while (currentVariable) { + if (!strcmp(currentVariable->variable, name)) + return currentVariable->value; + + currentVariable = currentVariable->next; + } + + return NULL; +} + +PR_IMPLEMENT(int) +_MD_PutEnv(const char *string) +{ + EnvVariable *currentVariable = gEnvironmentVariables; + char *variableCopy, + *value, + *current; + + variableCopy = strdup(string); + PR_ASSERT(variableCopy != NULL); + + current = variableCopy; + while (*current != '=') + current++; + + *current = 0; + current++; + + value = current; + + while (currentVariable) { + if (!strcmp(currentVariable->variable, variableCopy)) + break; + + currentVariable = currentVariable->next; + } + + if (currentVariable == NULL) { + currentVariable = PR_NEW(EnvVariable); + + if (currentVariable == NULL) { + PR_DELETE(variableCopy); + return PR_FALSE; + } + + currentVariable->variable = strdup(variableCopy); + currentVariable->value = strdup(value); + currentVariable->next = gEnvironmentVariables; + gEnvironmentVariables = currentVariable; + } + + else { + PR_DELETE(currentVariable->value); + currentVariable->value = strdup(current); + + /* This is a temporary hack. Working on a real fix, remove this when done. */ + /* OK, there are two ways to access the */ + /* library path, getenv() and PR_GetLibraryPath(). Take a look at PR_GetLibraryPath(). */ + /* You'll see that we keep the path in a global which is intialized at startup from */ + /* a call to getenv(). From then on, they have nothing in common. */ + /* We need to keep them in synch. */ + if (strcmp(currentVariable->variable, "LD_LIBRARY_PATH") == 0) + PR_SetLibraryPath(currentVariable->value); + } + + PR_DELETE(variableCopy); + return PR_TRUE; +} + + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark MISCELLANEOUS + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(t->md.jb); + } + *np = sizeof(t->md.jb) / sizeof(PRUint32); + return (PRWord*) (t->md.jb); +} + +void _MD_GetRegisters(PRUint32 *to) +{ + (void) setjmp((void*) to); +} + +void _MD_EarlyInit() +{ + Handle environmentVariables; + +#if !defined(MAC_NSPR_STANDALONE) + // MacintoshInitializeMemory(); Moved to mdmacmem.c: AllocateRawMemory(Size blockSize) +#else + MacintoshInitializeMemory(); +#endif + MacintoshInitializeTime(); + + // Install resource-controlled environment variables. + + environmentVariables = GetResource('Envi', 128); + if (environmentVariables != NULL) { + + Size resourceSize; + char *currentPutEnvString = (char *)*environmentVariables, + *currentScanChar = currentPutEnvString; + + resourceSize = GetHandleSize(environmentVariables); + DetachResource(environmentVariables); + HLock(environmentVariables); + + while (resourceSize--) { + + if ((*currentScanChar == '\n') || (*currentScanChar == '\r')) { + *currentScanChar = 0; + _MD_PutEnv (currentPutEnvString); + currentPutEnvString = currentScanChar + 1; + } + + currentScanChar++; + + } + + DisposeHandle(environmentVariables); + + } + +#ifdef PR_INTERNAL_LOGGING + _MD_PutEnv ("NSPR_LOG_MODULES=clock:6,cmon:6,io:6,mon:6,linker:6,cvar:6,sched:6,thread:6"); +#endif + +#if GENERATINGCFM + gStackSpacePatchCallThru = GetOSTrapAddress(0x0065); + SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchRD, 0x0065); + { + long foo; + foo = StackSpace(); + } +#else + gStackSpacePatchCallThru = GetOSTrapAddress(0x0065); + SetOSTrapAddress((UniversalProcPtr)&StackSpacePatchGlue, 0x0065); + StackSpace(); +#endif + + // THIS IS VERY IMPORTANT. Install our ExitToShell patch. + // This allows us to deactivate our Time Mananger task even + // if we are not totally gracefully exited. If this is not + // done then we will randomly crash at later times when the + // task is called after the app heap is gone. + + gExitToShellPatchCallThru = GetToolboxTrapAddress(0x01F4); + SetToolboxTrapAddress((UniversalProcPtr)&gExitToShellPatchRD, 0x01F4); + +} + +void _MD_FinalInit() +{ + _MD_InitNetAccess(); +} + +void PR_InitMemory(void) { +#ifndef NSPR_AS_SHARED_LIB + // Needed for Mac browsers without Java. We donÕt want them calling PR_INIT, since it + // brings in all of the thread support. But we do need to allow them to initialize + // the NSPR memory package. + // This should go away when all clients of the NSPR want threads AND memory. + MacintoshInitializeMemory(); +#endif +} + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark TERMINATION + +typedef pascal void (* ExitToShellProc)(void); + +// THIS IS *** VERY *** IMPORTANT... our ExitToShell patch. +// This allows us to deactivate our Time Mananger task even +// if we are not totally gracefully exited. If this is not +// done then we will randomly crash at later times when the +// task is called after the app heap is gone. + +extern TMTask gTimeManagerTaskElem; + +pascal void ExitToShellPatch(void) +{ + _MD_StopInterrupts(); + + CloseOpenTransport(); + +#if GENERATINGCFM + CallUniversalProc(gExitToShellPatchCallThru, uppExitToShellProcInfo); +#else + { + ExitToShellProc *exitProc = (ExitToShellProc *)&gExitToShellPatchCallThru; + (*exitProc)(); + } +#endif + +} + + + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark STRING OPERATIONS + +#if !defined(MAC_NSPR_STANDALONE) + +// PStrFromCStr converts the source C string to a destination +// pascal string as it copies. The dest string will +// be truncated to fit into an Str255 if necessary. +// If the C String pointer is NULL, the pascal string's length is set to zero +// +extern void +PStrFromCStr(const char* src, Str255 dst) +{ + short length = 0; + + // handle case of overlapping strings + if ( (void*)src == (void*)dst ) + { + unsigned char* curdst = &dst[1]; + unsigned char thisChar; + + thisChar = *(const unsigned char*)src++; + while ( thisChar != '\0' ) + { + unsigned char nextChar; + + // use nextChar so we don't overwrite what we are about to read + nextChar = *(const unsigned char*)src++; + *curdst++ = thisChar; + thisChar = nextChar; + + if ( ++length >= 255 ) + break; + } + } + else if ( src != NULL ) + { + unsigned char* curdst = &dst[1]; + short overflow = 255; // count down so test it loop is faster + register char temp; + + // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero + // which might overrun pascal buffer. Instead we use a temp variable. + while ( (temp = *src++) != 0 ) + { + *(char*)curdst++ = temp; + + if ( --overflow <= 0 ) + break; + } + length = 255 - overflow; + } + dst[0] = length; +} + + +void CStrFromPStr(ConstStr255Param pString, char **cString) +{ + // Allocates a cString and copies a Pascal string into it. + unsigned int len; + + len = pString[0]; + *cString = malloc(len+1); + + if (*cString != NULL) { + strncpy(*cString, (char *)&pString[1], len); + (*cString)[len] = NULL; + } +} + + +void dprintf(const char *format, ...) +{ +#if DEBUG + va_list ap; + char *buffer; + + va_start(ap, format); + buffer = PR_vsmprintf(format, ap); + va_end(ap); + + c2pstr(buffer); + DebugStr( (unsigned char *)buffer); + free(buffer); +#endif /* DEBUG */ +} + +#else + +void debugstr(const char *debuggerMsg) +{ + Str255 pStr; + + PStrFromCStr(debuggerMsg, pStr); + DebugStr(pStr); +} + + +char *strdup(const char *source) +{ + char *newAllocation; + size_t stringLength; + +#ifdef DEBUG + PR_ASSERT(source); +#endif + + stringLength = strlen(source) + 1; + + newAllocation = (char *)PR_MALLOC(stringLength); + if (newAllocation == NULL) + return NULL; + BlockMoveData(source, newAllocation, stringLength); + return newAllocation; +} + +// PStrFromCStr converts the source C string to a destination +// pascal string as it copies. The dest string will +// be truncated to fit into an Str255 if necessary. +// If the C String pointer is NULL, the pascal string's length is set to zero +// +void PStrFromCStr(const char* src, Str255 dst) +{ + short length = 0; + + // handle case of overlapping strings + if ( (void*)src == (void*)dst ) + { + unsigned char* curdst = &dst[1]; + unsigned char thisChar; + + thisChar = *(const unsigned char*)src++; + while ( thisChar != '\0' ) + { + unsigned char nextChar; + + // use nextChar so we don't overwrite what we are about to read + nextChar = *(const unsigned char*)src++; + *curdst++ = thisChar; + thisChar = nextChar; + + if ( ++length >= 255 ) + break; + } + } + else if ( src != NULL ) + { + unsigned char* curdst = &dst[1]; + short overflow = 255; // count down so test it loop is faster + register char temp; + + // Can't do the K&R C thing of Òwhile (*s++ = *t++)Ó because it will copy trailing zero + // which might overrun pascal buffer. Instead we use a temp variable. + while ( (temp = *src++) != 0 ) + { + *(char*)curdst++ = temp; + + if ( --overflow <= 0 ) + break; + } + length = 255 - overflow; + } + dst[0] = length; +} + + +void CStrFromPStr(ConstStr255Param pString, char **cString) +{ + // Allocates a cString and copies a Pascal string into it. + unsigned int len; + + len = pString[0]; + *cString = PR_MALLOC(len+1); + + if (*cString != NULL) { + strncpy(*cString, (char *)&pString[1], len); + (*cString)[len] = NULL; + } +} + + +size_t strlen(const char *source) +{ + size_t currentLength = 0; + + if (source == NULL) + return currentLength; + + while (*source++ != '\0') + currentLength++; + + return currentLength; +} + +int strcmpcore(const char *str1, const char *str2, int caseSensitive) +{ + char currentChar1, currentChar2; + + while (1) { + + currentChar1 = *str1; + currentChar2 = *str2; + + if (!caseSensitive) { + + if ((currentChar1 >= 'a') && (currentChar1 <= 'z')) + currentChar1 += ('A' - 'a'); + + if ((currentChar2 >= 'a') && (currentChar2 <= 'z')) + currentChar2 += ('A' - 'a'); + + } + + if (currentChar1 == '\0') + break; + + if (currentChar1 != currentChar2) + return currentChar1 - currentChar2; + + str1++; + str2++; + + } + + return currentChar1 - currentChar2; +} + +int strcmp(const char *str1, const char *str2) +{ + return strcmpcore(str1, str2, true); +} + +int strcasecmp(const char *str1, const char *str2) +{ + return strcmpcore(str1, str2, false); +} + +#if GENERATING68K +asm void *memset(void *target, int pattern, size_t length) // Legal asm qualifier +{ + MOVEA.L 4(SP),A0 // target -> A0 + MOVE.W 10(SP),D0 // pattern -> D0, length -> D1 + MOVE.L 12(SP),D1 + CMPI.L #30,D1 + BLT end + + // Fill D0 with the pattern + MOVEQ #0,D2 // Clear D2, weÕll use it as scratch + MOVE.B D0,D2 // Fill the bottom byte + LSL.W #8,D0 // + OR.W D0,D2 + MOVE.L D2,D0 + SWAP D2 + OR.L D2,D0 + + // Are we odd aligned? + MOVE.L A0,D2 // Copy target address into scratch + LSR.B #1,D2 // Sets C bit + BCC.S checkAlign2 // If even, check for 16-byte alignment + MOVE.B D0,(A0)+ // Take care of odd byte + SUBQ.L #1,D1 // Update length + + // Are we odd 16-byte word alligned? +checkAlign2: LSR.B #1,D2 // Still set from last check + BCC totallyAligned + MOVE.W D0,(A0)+ + SUBQ.L #2,D1 + +totallyAligned: MOVE.L D1,D2 + LSR.L #4,D2 + SUBQ.L #1,D2 +copyHunk: MOVE.L D0,(A0)+ + MOVE.L D0,(A0)+ + MOVE.L D0,(A0)+ + MOVE.L D0,(A0)+ + SUBQ.L #1,D2 + BCC copyHunk + ANDI.W #15,D1 // Check done? + BRA end +dribble: MOVE.B D0,(A0)+ +end: DBF D1,dribble + MOVE.L 4(SP),D0 // Return the target + RTS +} +#endif + +void *memcpy(void *to, const void *from, size_t size) +{ + if (size != 0) { +#if DEBUG + if ((UInt32)to < 0x1000) + DebugStr("\pmemcpy has illegal to argument"); + if ((UInt32)from < 0x1000) + DebugStr("\pmemcpy has illegal from argument"); +#endif + BlockMoveData(from, to, size); + } + return to; +} + +void dprintf(const char *format, ...) +{ + va_list ap; + char *buffer; + + va_start(ap, format); + buffer = (char *)PR_vsmprintf(format, ap); + va_end(ap); + + debugstr(buffer); + PR_DELETE(buffer); +} + +void +exit(int result) +{ +#pragma unused (result) + + ExitToShell(); +} + +void abort(void) +{ + exit(-1); +} + +#endif + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark FLUSHING THE GARBAGE COLLECTOR + +#if !defined(MAC_NSPR_STANDALONE) + +unsigned char GarbageCollectorCacheFlusher(PRUint32) +{ + + PRIntn is; + + UInt32 oldPriority; + + // If java wasn't completely initialized, then bail + // harmlessly. + + if (PR_GetGCInfo()->lock == NULL) + return false; + +#if DEBUG + if (_MD_GET_INTSOFF() == 1) + DebugStr("\pGarbageCollectorCacheFlusher at interrupt time!"); +#endif + + // The synchronization here is very tricky. We really + // don't want any other threads to run while we are + // cleaning up the gc heap... they could call malloc, + // and then we would be in trouble in a big way. So, + // we jack up our priority and that of the finalizer + // so that we won't yield to other threads. + // dkc 5/17/96 + + oldPriority = PR_GetThreadPriority(PR_GetCurrentThread()); + _PR_INTSOFF(is); + _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)30); + _PR_INTSON(is); + + // Garbage collect twice. This will finalize any + // dangling AWT resources (images, components), and + // then free up their GC space, too. + // dkc 2/15/96 + // interrupts must be on during PR_GC + + PR_GC(); + + // By setting the finalizer priority to 31, then we + // ensure it will run before us. When it finishes + // its list of finalizations, it returns to us + // for the second garbage collection. + + PR_Yield(); + + PR_GC(); + + // Restore our old priorities. + + _PR_INTSOFF(is); + _PR_SetThreadPriority(PR_GetCurrentThread(), (PRThreadPriority)oldPriority); + _PR_INTSON(is); + + return false; +} + +#endif + +//############################################################################## +//############################################################################## +#pragma mark - +#pragma mark MISCELLANEOUS-HACKS + + +// +// ***** HACK FIX THESE **** +// +extern long _MD_GetOSName(char *buf, long count) +{ + long len; + + len = PR_snprintf(buf, count, "Mac OS"); + + return 0; +} + +extern long _MD_GetOSVersion(char *buf, long count) +{ + long len; + + len = PR_snprintf(buf, count, "7.5"); + + return 0; +} + +extern long _MD_GetArchitecture(char *buf, long count) +{ + long len; + +#if defined(GENERATINGPOWERPC) && GENERATINGPOWERPC + len = PR_snprintf(buf, count, "PowerPC"); +#else + len = PR_snprintf(buf, count, "Motorola68k"); +#endif + + return 0; +} diff --git a/pr/src/md/mac/prcpucfg.h b/pr/src/md/mac/prcpucfg.h new file mode 100644 index 00000000..88c9797f --- /dev/null +++ b/pr/src/md/mac/prcpucfg.h @@ -0,0 +1,115 @@ +/* -*- 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. + */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_MAC +#define XP_MAC +#endif + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1L +#define PR_BYTES_PER_SHORT 2L +#define PR_BYTES_PER_INT 4L +#define PR_BYTES_PER_INT64 8L +#define PR_BYTES_PER_LONG 4L +#define PR_BYTES_PER_FLOAT 4L +#define PR_BYTES_PER_DOUBLE 8L +#define PR_BYTES_PER_WORD 4L +#define PR_BYTES_PER_DWORD 8L + +#define PR_BITS_PER_BYTE 8L +#define PR_BITS_PER_SHORT 16L +#define PR_BITS_PER_INT 32L +#define PR_BITS_PER_INT64 64L +#define PR_BITS_PER_LONG 32L +#define PR_BITS_PER_FLOAT 32L +#define PR_BITS_PER_DOUBLE 64L +#define PR_BITS_PER_WORD 32L + +#define PR_BITS_PER_BYTE_LOG2 3L +#define PR_BITS_PER_SHORT_LOG2 4L +#define PR_BITS_PER_INT_LOG2 5L +#define PR_BITS_PER_INT64_LOG2 6L +#define PR_BITS_PER_LONG_LOG2 5L +#define PR_BITS_PER_FLOAT_LOG2 5L +#define PR_BITS_PER_DOUBLE_LOG2 6L +#define PR_BITS_PER_WORD_LOG2 5L + +#define PR_ALIGN_OF_SHORT 2L +#define PR_ALIGN_OF_INT 4L +#define PR_ALIGN_OF_LONG 4L +#define PR_ALIGN_OF_INT64 2L +#define PR_ALIGN_OF_FLOAT 4L +#define PR_ALIGN_OF_DOUBLE 4L +#define PR_ALIGN_OF_POINTER 4L +#define PR_ALIGN_OF_WORD 4L + +#define PR_BYTES_PER_WORD_LOG2 2L +#define PR_BYTES_PER_DWORD_LOG2 3L +#define PR_WORDS_PER_DWORD_LOG2 1L + +#define _PR_LOCAL_THREADS_ONLY + +#ifndef NO_NSPR_10_SUPPORT +#define BYTES_PER_BYTE PR_BYTES_PER_BYTE +#define BYTES_PER_SHORT PR_BYTES_PER_SHORT +#define BYTES_PER_INT PR_BYTES_PER_INT +#define BYTES_PER_INT64 PR_BYTES_PER_INT64 +#define BYTES_PER_LONG PR_BYTES_PER_LONG +#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT +#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE +#define BYTES_PER_WORD PR_BYTES_PER_WORD +#define BYTES_PER_DWORD PR_BYTES_PER_DWORD + +#define BITS_PER_BYTE PR_BITS_PER_BYTE +#define BITS_PER_SHORT PR_BITS_PER_SHORT +#define BITS_PER_INT PR_BITS_PER_INT +#define BITS_PER_INT64 PR_BITS_PER_INT64 +#define BITS_PER_LONG PR_BITS_PER_LONG +#define BITS_PER_FLOAT PR_BITS_PER_FLOAT +#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE +#define BITS_PER_WORD PR_BITS_PER_WORD + +#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 +#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 +#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 +#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 +#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 +#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 +#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 +#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 + +#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT +#define ALIGN_OF_INT PR_ALIGN_OF_INT +#define ALIGN_OF_LONG PR_ALIGN_OF_LONG +#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 +#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT +#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE +#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER +#define ALIGN_OF_WORD PR_ALIGN_OF_WORD + +#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 +#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 +#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 +#endif NO_NSPR_10_SUPPORT + +#endif /* nspr_cpucfg___ */ diff --git a/pr/src/md/os2/Makefile b/pr/src/md/os2/Makefile new file mode 100644 index 00000000..1b23db00 --- /dev/null +++ b/pr/src/md/os2/Makefile @@ -0,0 +1,51 @@ +# +# 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), OS2) +CSRCS = \ + os2misc.c \ + os2sem.c \ + os2inrval.c \ + os2gc.c \ + os2thred.c \ + os2io.c \ + os2cv.c \ + os2sock.c \ + os2_errors.c \ + os2poll.c \ + $(NULL) +endif + +TARGETS = $(OBJS) + +INCLUDES = -I$(DIST)/include/private -I$(DIST)/include + +include $(MOD_DEPTH)/config/rules.mk + +export:: $(TARGETS) + @cmd /C "copy *.obj $(OBJDIR)\*.o > nul" + +install:: export + + + diff --git a/pr/src/md/os2/os2_errors.c b/pr/src/md/os2/os2_errors.c new file mode 100644 index 00000000..33f1a062 --- /dev/null +++ b/pr/src/md/os2/os2_errors.c @@ -0,0 +1,1053 @@ +/* -*- 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 "primpl.h" + +void _MD_os2_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: + case ERROR_INVALID_ACCESS: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ERROR_INVALID_NAME: + case ERROR_INVALID_PARAMETER: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ERROR_TOO_MANY_OPEN_FILES: + case ERROR_NOT_DOS_DISK: + case ERROR_NOT_READY: + case ERROR_OPEN_FAILED: + case ERROR_PATH_BUSY: + case ERROR_CANNOT_MAKE: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_DRIVE_LOCKED: + case ERROR_DEVICE_IN_USE: + 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_SHARING_BUFFER_EXCEEDED: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_closedir_error(PRInt32 err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_ACCESS_DENIED: + 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_NOT_FOUND: + 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_DOS_DISK: + case ERROR_LOCK_VIOLATION: + case ERROR_BROKEN_PIPE: + case ERROR_NOT_READY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_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_os2_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_os2_map_fstat_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + 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_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + 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_os2_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_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_MORE_DATA: + 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_os2_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_os2_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_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_MORE_DATA: + 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_os2_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_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_MORE_DATA: + 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_os2_map_read_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + 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_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_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 EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_transmitfile_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + 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_READY: + case ERROR_PATH_BUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_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 EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_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_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_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_MORE_DATA: + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ERROR_NETNAME_DELETED: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_lseek_error(PRInt32 err) +{ + switch (err) { + 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_os2_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_INVALID_HANDLE: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + 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_os2_map_close_error(PRInt32 err) +{ + switch (err) { + case ERROR_INVALID_HANDLE: + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + 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_os2_map_socket_error(PRInt32 err) +{ + switch (err) { + case EPROTONOSUPPORT: + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_MORE_DATA: + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_recv_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + 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_os2_map_recvfrom_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + 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_os2_map_send_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case SOCEFAULT: + 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_os2_map_sendto_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case SOCEFAULT: + 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_os2_map_accept_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_acceptex_error(PRInt32 err) +{ + switch (err) { + 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_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_connect_error(PRInt32 err) +{ + switch (err) { + case EWOULDBLOCK: + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EINPROGRESS: + case EALREADY: + case EINVAL: + PR_SetError(PR_ALREADY_INITIATED_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EAFNOSUPPORT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case ENETUNREACH: + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_bind_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_listen_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_shutdown_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_getsockname_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_getpeername_error(PRInt32 err) +{ + + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_getsockopt_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_setsockopt_error(PRInt32 err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_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_NOT_READY: + case ERROR_OPEN_FAILED: + 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_MORE_DATA: + 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_os2_map_gethostname_error(PRInt32 err) +{ + switch (err) { + case SOCEFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENETDOWN: + case EINPROGRESS: + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_os2_map_select_error(PRInt32 err) +{ + PRErrorCode prerror; + + switch (err) { + /* + * OS/2 select() only works on sockets. So in this + * context, ENOTSOCK is equivalent to EBADF on Unix. + */ + case ENOTSOCK: + prerror = PR_BAD_DESCRIPTOR_ERROR; + break; + case EINVAL: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + case SOCEFAULT: + prerror = PR_ACCESS_FAULT_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + } + PR_SetError(prerror, err); +} + +void _MD_os2_map_lockf_error(PRInt32 err) +{ + switch (err) { + case ERROR_ACCESS_DENIED: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + 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_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_MORE_DATA: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} diff --git a/pr/src/md/os2/os2cv.c b/pr/src/md/os2/os2cv.c new file mode 100644 index 00000000..34264dd5 --- /dev/null +++ b/pr/src/md/os2/os2cv.c @@ -0,0 +1,332 @@ +/* -*- 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. + */ + +/* + * os2cv.c -- OS/2 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) + memset(&lock->notified, 0, 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 = DosPostEventSem(thred->md.blocked_sema.sem); + PR_ASSERT(rv == NO_ERROR); + 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. + * + */ +PR_IMPLEMENT(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; +} + +PR_IMPLEMENT(void) _PR_MD_FREE_CV(_MDCVar *cv) +{ + cv->magic = (PRUint32)-1; + return; +} + +/* + * _PR_MD_WAIT_CV() -- Wait on condition variable + */ +PR_IMPLEMENT(void) +_PR_MD_WAIT_CV(_MDCVar *cv, _MDLock *lock, PRIntervalTime timeout ) +{ + PRThread *thred = _PR_MD_CURRENT_THREAD(); + ULONG rv, count; + ULONG msecs = (timeout == PR_INTERVAL_NO_TIMEOUT) ? + SEM_INDEFINITE_WAIT : 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 = DosWaitEventSem(thred->md.blocked_sema.sem, msecs); + DosResetEventSem(thred->md.blocked_sema.sem, &count); + + EnterCriticalSection(&(lock->mutex)); + + PR_ASSERT(rv == NO_ERROR || rv == ERROR_TIMEOUT); + + if(rv == ERROR_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 + * SemRelease call happens after SemRequest + * times out. Wait on the semaphore again to make it + * non-signaled. We assume this wait won't take long. + */ + rv = DosWaitEventSem(thred->md.blocked_sema.sem, SEM_INDEFINITE_WAIT); + DosResetEventSem(thred->md.blocked_sema.sem, &count); + PR_ASSERT(rv == NO_ERROR); + } + } + PR_ASSERT(thred->md.inCVWaitQueue == PR_FALSE); + return; +} /* --- end _PR_MD_WAIT_CV() --- */ + +PR_IMPLEMENT(void) +_PR_MD_NOTIFY_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_FALSE); + return; +} + +PR_IMPLEMENT(void) +_PR_MD_NOTIFYALL_CV(_MDCVar *cv, _MDLock *lock) +{ + md_PostNotifyToCvar(cv, lock, PR_TRUE); + return; +} + +PR_IMPLEMENT(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/os2/os2gc.c b/pr/src/md/os2/os2gc.c new file mode 100644 index 00000000..00db7e15 --- /dev/null +++ b/pr/src/md/os2/os2gc.c @@ -0,0 +1,73 @@ +/* -*- 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 "primpl.h" + +extern APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + CONTEXTRECORD context; + context.ContextFlags = CONTEXT_INTEGER; + + if (_PR_IS_NATIVE_THREAD(t)) { + context.ContextFlags |= CONTEXT_CONTROL; + if (QueryThreadContext(t->md.handle, CONTEXT_CONTROL, &context)) { + t->md.gcContext[0] = context.ctx_RegEax; + t->md.gcContext[1] = context.ctx_RegEbx; + t->md.gcContext[2] = context.ctx_RegEcx; + t->md.gcContext[3] = context.ctx_RegEdx; + t->md.gcContext[4] = context.ctx_RegEsi; + t->md.gcContext[5] = context.ctx_RegEdi; + t->md.gcContext[6] = context.ctx_RegEsp; + t->md.gcContext[7] = context.ctx_RegEbp; + *np = PR_NUM_GCREGS; + } else { + PR_ASSERT(0);/* XXX */ + } + } + return (PRWord *)&t->md.gcContext; +} + +/* 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() +{ + void *fiberData = 0; + + /* 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. + */ +#ifdef HAVE_ASM + __asm { + mov EDX, FS:[18h] + mov EAX, DWORD PTR [EDX+10h] + mov [fiberData], EAX + } +#endif + + return fiberData; +} diff --git a/pr/src/md/os2/os2inrval.c b/pr/src/md/os2/os2inrval.c new file mode 100644 index 00000000..a204a639 --- /dev/null +++ b/pr/src/md/os2/os2inrval.c @@ -0,0 +1,81 @@ +/* -*- 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. + */ + +/* + * OS/2 interval timers + * + */ + +#include "primpl.h" + +ULONG _os2_ticksPerSec = -1; +PRIntn _os2_bitShift = 0; +PRInt32 _os2_highMask = 0; + + + +PR_IMPLEMENT(void) +_PR_MD_INTERVAL_INIT() +{ + ULONG count; + + if (DosTmrQueryFreq(&_os2_ticksPerSec) == NO_ERROR) + { + while(_os2_ticksPerSec > PR_INTERVAL_MAX) { + _os2_ticksPerSec >>= 1; + _os2_bitShift++; + _os2_highMask = (_os2_highMask << 1)+1; + } + } + else + _os2_ticksPerSec = -1; + + PR_ASSERT(_os2_ticksPerSec > PR_INTERVAL_MIN && _os2_ticksPerSec < PR_INTERVAL_MAX); +} + +PR_IMPLEMENT(PRIntervalTime) +_PR_MD_GET_INTERVAL() +{ + QWORD count; + + /* Sadly; nspr requires the interval to range from 1000 ticks per second + * to only 100000 ticks per second; Counter is too high + * resolution... + */ + if (DosTmrQueryTime(&count) == NO_ERROR) { + PRInt32 top = count.ulHi & _os2_highMask; + top = top << (32 - _os2_bitShift); + count.ulLo = count.ulLo >> _os2_bitShift; + count.ulHi = count.ulLo + top; + return (PRUint32)count.ulLo; + } + else{ + ULONG msCount = PR_FAILURE; + DosQuerySysInfo(QSV_MS_COUNT, QSV_MS_COUNT, &msCount, sizeof(msCount)); + return msCount; + } +} + +PR_IMPLEMENT(PRIntervalTime) +_PR_MD_INTERVAL_PER_SEC() +{ + if(_os2_ticksPerSec != -1) + return _os2_ticksPerSec; + else + return 1000; +} diff --git a/pr/src/md/os2/os2io.c b/pr/src/md/os2/os2io.c new file mode 100644 index 00000000..b2e89d27 --- /dev/null +++ b/pr/src/md/os2/os2io.c @@ -0,0 +1,620 @@ +/* -*- 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. + */ + +/* OS2 IO module + * + * Assumes synchronous I/O. + * + */ + +#include "primpl.h" +#include <direct.h> + +struct _MDLock _pr_ioq_lock; + +PR_IMPLEMENT(PRStatus) +_PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PRInt32 rv; + ULONG count; + + PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? + SEM_INDEFINITE_WAIT : PR_IntervalToMilliseconds(ticks); + rv = DosWaitEventSem(thread->md.blocked_sema.sem, msecs); + DosResetEventSem(thread->md.blocked_sema.sem, &count); + switch(rv) + { + case NO_ERROR: + return PR_SUCCESS; + break; + case ERROR_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 SemRequest() to clear the semaphore. + */ + _PR_THREAD_UNLOCK(thread); + rv = DosWaitEventSem(thread->md.blocked_sema.sem, 0); + DosResetEventSem(thread->md.blocked_sema.sem, &count); + PR_ASSERT(rv == NO_ERROR); + } + } + return PR_SUCCESS; + break; + default: + break; + } + return PR_FAILURE; +} +PR_IMPLEMENT(PRStatus) +_PR_MD_WAKEUP_WAITER(PRThread *thread) +{ + if ( _PR_IS_NATIVE_THREAD(thread) ) + { + if (DosPostEventSem(thread->md.blocked_sema.sem) != NO_ERROR) + 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 OS/2 + * + * 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) +{ + HFILE file; + PRInt32 access = OPEN_SHARE_DENYNONE; + PRInt32 flags = OPEN_ACTION_OPEN_IF_EXISTS; + PRInt32 rc; + PRUword actionTaken; + + ULONG CurMaxFH = 0; + LONG ReqCount = 1; + + if (osflags & PR_RDONLY) + access |= OPEN_ACCESS_READONLY; + else if (osflags & PR_WRONLY) + access |= OPEN_ACCESS_WRITEONLY; + else if(osflags & PR_RDWR) + access |= OPEN_ACCESS_READWRITE; + if (osflags & PR_CREATE_FILE) + flags |= OPEN_ACTION_CREATE_IF_NEW; + else if (osflags & PR_TRUNCATE){ + flags &= ~OPEN_ACTION_OPEN_IF_EXISTS; + flags |= OPEN_ACTION_REPLACE_IF_EXISTS; + } + + /* OS/2 sets the Max file handles per process to 20 by default */ + DosSetRelMaxFH(&ReqCount, &CurMaxFH); + + rc = DosOpen((char*)name, + &file, /* file handle if successful */ + &actionTaken, /* reason for failure */ + 0, /* initial size of new file */ + FILE_NORMAL, /* file system attributes */ + flags, /* Open flags */ + access, /* Open mode and rights */ + 0); /* OS/2 Extended Attributes */ + if (rc != NO_ERROR) { + _PR_MD_MAP_OPEN_ERROR(rc); + return -1; + } + + return (PRInt32)file; +} + +PRInt32 +_PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) +{ + PRUword bytes; + int rv; + + rv = DosRead((HFILE)fd->secret->md.osfd, + (PVOID)buf, + len, + &bytes); + + if (rv != NO_ERROR) + { + /* ERROR_HANDLE_EOF can only be returned by async io */ + PR_ASSERT(rv != ERROR_HANDLE_EOF); + if (rv == ERROR_BROKEN_PIPE) + return 0; + else { + _PR_MD_MAP_READ_ERROR(rv); + return -1; + } + } + return bytes; +} + +PRInt32 +_PR_MD_WRITE(PRFileDesc *fd, const void *buf, PRInt32 len) +{ + PRUword bytes; + int rv; + + /* No longer using DosWrite since it doesn't convert \n to \n\r like C runtime does */ +#if 0 + rv = DosWrite((HFILE)fd->secret->md.osfd, + (PVOID)buf, + len, + &bytes); + + if (rv != NO_ERROR) + { + _PR_MD_MAP_WRITE_ERROR(rv); + return -1; + } +#else + bytes = write(fd->secret->md.osfd, buf, len); + if (rv == -1) + _PR_MD_MAP_WRITE_ERROR(errno); +#endif + + return bytes; +} /* --- end _PR_MD_WRITE() --- */ + +PRInt32 +_PR_MD_LSEEK(PRFileDesc *fd, PRInt32 offset, int whence) +{ + PRInt32 rv; + PRUword newLocation; + + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, offset, whence, &newLocation); + + if (rv != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(rv); + return -1; + } else + return newLocation; +} + +PRInt64 +_PR_MD_LSEEK64(PRFileDesc *fd, PRInt64 offset, int whence) +{ + PRInt64 result; + PRInt32 rv, low = offset.lo, hi = offset.hi; + PRUword newLocation; + + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, low, whence, &newLocation); + rv = DosSetFilePtr((HFILE)fd->secret->md.osfd, hi, FILE_CURRENT, &newLocation); + + if (rv != NO_ERROR) { + _PR_MD_MAP_LSEEK_ERROR(rv); + hi = newLocation = -1; + } + + result.lo = hi; + result.hi = newLocation; + return result; +} + +PRInt32 +_PR_MD_FSYNC(PRFileDesc *fd) +{ + PRInt32 rc = DosResetBuffer((HFILE)fd->secret->md.osfd); + + if (rc != NO_ERROR) { + if (rc != ERROR_ACCESS_DENIED) { + _PR_MD_MAP_FSYNC_ERROR(rc); + return -1; + } + } + return 0; +} + +PRInt32 +_MD_CloseFile(PRInt32 osfd) +{ + PRInt32 rv; + + rv = DosClose((HFILE)osfd); + if (rv != NO_ERROR) + _PR_MD_MAP_CLOSE_ERROR(rv); + return rv; +} + + +/* --- DIR IO ------------------------------------------------------------ */ +#define GetFileFromDIR(d) (d)->d_entry.achName + +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 VAC RTL. +** +*/ + +PRInt32 +_PR_MD_CLOSE_DIR(_MDDir *d) +{ + PRInt32 rc; + + if ( d ) { + rc = DosFindClose(d->d_hdl); + if(rc == NO_ERROR){ + d->magic = (PRUint32)-1; + return PR_SUCCESS; + } else { + _PR_MD_MAP_CLOSEDIR_ERROR(rc); + 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[ CCHMAXPATH ]; + PRUword numEntries, rc; + + PR_snprintf(filename, CCHMAXPATH, "%s%s%s", + name, PR_DIRECTORY_SEPARATOR_STR, "*.*"); + FlipSlashes( filename, strlen(filename) ); + + d->d_hdl = HDIR_CREATE; + + rc = DosFindFirst( filename, &d->d_hdl, FILE_DIRECTORY, &(d->d_entry), sizeof(d->d_entry), &numEntries, FIL_STANDARD); + if ( rc != NO_ERROR ) { + _PR_MD_MAP_OPENDIR_ERROR(rc); + return PR_FAILURE; + } + d->firstEntry = PR_TRUE; + d->magic = _MD_MAGIC_DIR; + return PR_SUCCESS; +} + +char * +_PR_MD_READ_DIR(_MDDir *d, PRIntn flags) +{ + PRUword numFiles = 1; + BOOL rv; + char *fileName; + + if ( d ) { + while (1) { + if (d->firstEntry) { + d->firstEntry = PR_FALSE; + rv = NO_ERROR; + } else { + rv = DosFindNext(d->d_hdl, &(d->d_entry), sizeof(d->d_entry), &numFiles); + } + if (rv != NO_ERROR) { + 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; + /* + * XXX + * Is this the correct definition of a hidden file on OS/2? + */ + if ( (flags & PR_SKIP_HIDDEN) && (fileName[0] == '.')) + continue; + return fileName; + } + PR_ASSERT(NO_ERROR != rv); + _PR_MD_MAP_READDIR_ERROR(rv); + return NULL; + } + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; +} + +PRInt32 +_PR_MD_DELETE(const char *name) +{ + PRInt32 rc = DosDelete((char*)name); + if(rc == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_DELETE_ERROR(rc); + return -1; + } +} + +PRInt32 +_PR_MD_STAT(const char *fn, struct stat *info) +{ + PRInt32 rv; + + rv = _stat((char*)fn, 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. + * + * Not sure if this happens on OS/2 or not, + * but it doesn't hurt to be careful. + */ + + 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, info); + } + } + + if (-1 == rv) { + _PR_MD_MAP_STAT_ERROR(errno); + } + return rv; +} + +PRInt32 +_PR_MD_GETFILEINFO(const char *fn, PRFileInfo *info) +{ + struct stat sb; + PRInt32 rv; + + if ( (rv = _PR_MD_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; + info->modifyTime.lo = sb.st_mtime; + info->creationTime.lo = sb.st_ctime; + } + } + 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->size.lo = info32.size; + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + } + return rv; +} + +PRInt32 +_PR_MD_GETOPENFILEINFO(const PRFileDesc *fd, PRFileInfo *info) +{ + /* For once, the VAC compiler/library did a nice thing. + * The file handle used by the C runtime is the same one + * returned by the OS when you call DosOpen(). This means + * that you can take an OS HFILE and use it with C file + * functions. The only caveat is that you have to call + * _setmode() first to initialize some junk. This is + * immensely useful because I did not have a clue how to + * implement this function otherwise. The windows folks + * took the source from the Microsoft C library source, but + * IBM wasn't kind enough to ship the source with VAC. + * On second thought, the needed function could probably + * be gotten from the OS/2 GNU library source, but the + * point is now moot. + */ + struct stat hinfo; + + _setmode(fd->secret->md.osfd, O_BINARY); + if(fstat((int)fd->secret->md.osfd, &hinfo) != NO_ERROR) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + + if (hinfo.st_mode & S_IFDIR) + info->type = PR_FILE_DIRECTORY; + else + info->type = PR_FILE_FILE; + + info->size = hinfo.st_size; + info->modifyTime.lo = hinfo.st_mtime; + info->creationTime.lo = hinfo.st_ctime; + + return 0; +} + +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->size.lo = info32.size; + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + } + return rv; +} + + +PRInt32 +_PR_MD_RENAME(const char *from, const char *to) +{ + PRInt32 rc; + /* Does this work with dot-relative pathnames? */ + if ( (rc = DosMove((char *)from, (char *)to)) == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_RENAME_ERROR(rc); + 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) +{ + PRInt32 rc; + /* XXXMB - how to translate the "mode"??? */ + if ((rc = DosCreateDir((char *)name, NULL)) == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_MKDIR_ERROR(rc); + return -1; + } +} + +PRInt32 +_PR_MD_RMDIR(const char *name) +{ + PRInt32 rc; + if ( (rc = DosDeleteDir((char *)name)) == NO_ERROR) { + return 0; + } else { + _PR_MD_MAP_RMDIR_ERROR(rc); + return -1; + } +} + +PR_IMPLEMENT(PRStatus) +_PR_MD_LOCKFILE(PRInt32 f) +{ + PRInt32 rv; + FILELOCK lock, unlock; + + lock.lOffset = 0; + lock.lRange = 0xffffffff; + unlock.lOffset = 0; + unlock.lRange = 0; + + /* + * loop trying to DosSetFileLocks(), + * pause for a few miliseconds when can't get the lock + * and try again + */ + for( rv = FALSE; rv == FALSE; /* do nothing */ ) + { + + rv = DosSetFileLocks( (HFILE) f, + &unlock, &lock, + 0, 0); + if ( rv != NO_ERROR ) + { + DosSleep( 50 ); /* Sleep() a few milisecs and try again. */ + } + } /* end for() */ + return PR_SUCCESS; +} /* end _PR_MD_LOCKFILE() */ + +PR_IMPLEMENT(PRStatus) +_PR_MD_TLOCKFILE(PRInt32 f) +{ + return _PR_MD_LOCKFILE(f); +} /* end _PR_MD_TLOCKFILE() */ + + +PR_IMPLEMENT(PRStatus) +_PR_MD_UNLOCKFILE(PRInt32 f) +{ + PRInt32 rv; + FILELOCK lock, unlock; + + lock.lOffset = 0; + lock.lRange = 0; + unlock.lOffset = 0; + unlock.lRange = 0xffffffff; + + rv = DosSetFileLocks( (HFILE) f, + &unlock, &lock, + 0, 0); + + if ( rv != NO_ERROR ) + { + return PR_SUCCESS; + } + else + { + return PR_FAILURE; + } +} /* end _PR_MD_UNLOCKFILE() */ + diff --git a/pr/src/md/os2/os2misc.c b/pr/src/md/os2/os2misc.c new file mode 100644 index 00000000..00913013 --- /dev/null +++ b/pr/src/md/os2/os2misc.c @@ -0,0 +1,513 @@ +/* -*- 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. + */ + +/* + * os2misc.c + * + */ +#include "primpl.h" + +PR_IMPLEMENT(char *) +_PR_MD_GET_ENV(const char *name) +{ + return getenv(name); +} + +PR_IMPLEMENT(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 OS/2. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(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; +} + + +/* + *********************************************************************** + *********************************************************************** + * + * 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, a null between + * the EXE name and the arguments, and 2 nulls at the end + * of command line. + */ + cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ + + 2 /* we quote every argument */ + + 4; /* space in between, or final nulls */ + } + 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 + 1) { + *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++ = '"'; + } + if(arg == argv) + *p++ = '\0'; + } + + /* Add 2 nulls at the end */ + *p++ = '\0'; + *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; + + PPIB ppib = NULL; + PTIB ptib = NULL; + + if (envp == NULL) { + *envBlock = NULL; + return 0; + } + + if(DosGetInfoBlocks(&ptib, &ppib) != NO_ERROR) + return -1; + + curEnv = ppib->pib_pchenv; + + 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) { + return -1; + } + + q = cwdStart; + while (q < cwdEnd) { + *p++ = *q++; + } + + 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_CreateOS2Process( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + char szFailed[CCHMAXPATH]; + RESULTCODES procInfo; + APIRET retVal; + char *cmdLine = NULL; + char *envBlock = NULL; + char **newEnvp; + PRProcess *proc = NULL; + HFILE hStdIn = 0, + hStdOut = 0, + hStdErr = 0; + + + 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; + } + + if (attr) { + PRBool redirected = PR_FALSE; + + /* On OS/2, there is really no way to pass file handles for stdin, stdout, + * and stderr to a new process. Instead, we can make it a child process + * and make the given file handles a copy of our stdin, stdout, and stderr. + * The child process then inherits ours, and we set ours back. Twisted + * and gross I know. If you know a better way, please use it. + */ + if (attr->stdinFd) { + hStdIn = (HFILE) attr->stdinFd->secret->md.osfd; + DosDupHandle(0, &hStdIn); + } + if (attr->stdoutFd) { + hStdOut = (HFILE) attr->stdoutFd->secret->md.osfd; + DosDupHandle(1, &hStdOut); + } + if (attr->stderrFd) { + hStdErr = (HFILE) attr->stderrFd->secret->md.osfd; + DosDupHandle(2, &hStdErr); + } + } + + retVal = DosExecPgm(szFailed, + CCHMAXPATH, + EXEC_ASYNCRESULT, + cmdLine, + envBlock, + &procInfo, + argv[0]); + + /* Restore our old values. Hope this works */ + if(hStdIn){ + hStdIn = 0; + DosDupHandle(0, &hStdIn); + } + if(hStdOut){ + hStdOut = 1; + DosDupHandle(1, &hStdOut); + } + if(hStdErr){ + hStdErr = 1; + DosDupHandle(0, &hStdErr); + } + + if (retVal != NO_ERROR) { + /* XXX what error code? */ + PR_SetError(PR_UNKNOWN_ERROR, retVal); + goto errorExit; + } + + proc->md.pid = procInfo.codeTerminate; + + 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_DetachOS2Process(PRProcess *process) +{ + /* This is basically what they did on Windows (CloseHandle) + * but I don't think it will do much on OS/2. A process is + * either created as a child or not. You can't 'detach' it + * later on. + */ + DosClose(process->md.pid); + PR_DELETE(process); + return PR_SUCCESS; +} + +/* + * XXX: This will currently only work on a child process. + */ +PRStatus _PR_WaitOS2Process(PRProcess *process, + PRInt32 *exitCode) +{ + ULONG ulRetVal; + RESULTCODES results; + PID pidEnded = 0; + + ulRetVal = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, + &results, + &pidEnded, process->md.pid); + + if (ulRetVal != NO_ERROR) { + printf("\nDosWaitChild rc = %i\n", ulRetVal); + PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); + return PR_FAILURE; + } + PR_DELETE(process); + return PR_SUCCESS; +} + +PRStatus _PR_KillOS2Process(PRProcess *process) +{ + ULONG ulRetVal; + if ((ulRetVal = DosKillProcess(DKP_PROCESS, process->md.pid)) == NO_ERROR) { + return PR_SUCCESS; + } + PR_SetError(PR_UNKNOWN_ERROR, ulRetVal); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) _MD_OS2GetHostName(char *name, PRUint32 namelen) +{ + PRIntn rv; + PRInt32 syserror; + + rv = gethostname(name, (PRInt32) namelen); + if (0 == rv) { + return PR_SUCCESS; + } + _PR_MD_MAP_GETHOSTNAME_ERROR(sock_errno()); + return PR_FAILURE; +} + +PR_IMPLEMENT(void) +_PR_MD_WAKEUP_CPUS( void ) +{ + return; +} + + +/* + ********************************************************************** + * + * Memory-mapped files are not supported on OS/2 (or 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/os2/os2poll.c b/pr/src/md/os2/os2poll.c new file mode 100644 index 00000000..b3353aa7 --- /dev/null +++ b/pr/src/md/os2/os2poll.c @@ -0,0 +1,165 @@ +/* -*- 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 OS/2. + */ + +#include "primpl.h" + +PRInt32 +_PR_MD_PR_POLL(PRPollDesc *pds, PRIntn npds, + PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRInt32 n, err, pdcnt; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + fd_set rd, wt, ex; + struct timeval tv, *tvp = NULL; + int maxfd = -1; + + /* + * For restarting _MD_SELECT() if it is interrupted by a signal. + * We use these variables to figure out how much time has elapsed + * and how much of the timeout still remains. + */ + PRIntervalTime start, elapsed, remaining; + + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); + + 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; + + if (osfd > maxfd) { + maxfd = 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; + start = PR_IntervalNow(); + } + +retry: + n = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp); + if (n == -1 && errno == EINTR) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } else { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + n = 0; /* timed out */ + } else { + remaining = timeout - elapsed; + tv.tv_sec = PR_IntervalToSeconds(remaining); + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - PR_SecondsToInterval(tv.tv_sec)); + goto retry; + } + } + } + + if (n > 0) { + n = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRInt32 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 = 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 < 0) { + err = _MD_ERRNO(); + if (err == EBADF) { + /* 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(_MD_ERRNO() == ENOTSOCK); + if (_MD_ERRNO() == ENOTSOCK) { + pd->out_flags = PR_POLL_NVAL; + n++; + } + } + } + PR_ASSERT(n > 0); + } else { + PR_ASSERT(err != EINTR); /* should have been handled above */ + _PR_MD_MAP_SELECT_ERROR(err); + } + } + return n; + } + diff --git a/pr/src/md/os2/os2sem.c b/pr/src/md/os2/os2sem.c new file mode 100644 index 00000000..684ca945 --- /dev/null +++ b/pr/src/md/os2/os2sem.c @@ -0,0 +1,74 @@ +/* -*- 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. + */ + +/* + * OS/2-specific semaphore handling code. + * + */ + +#include "primpl.h" + + +PR_IMPLEMENT(void) +_PR_MD_NEW_SEM(_MDSemaphore *md, PRUintn value) +{ + int rv; + + /* Our Sems don't support a value > 1 */ + PR_ASSERT(value <= 1); + + rv = DosCreateEventSem(NULL, &md->sem, 0, 0); + PR_ASSERT(rv == NO_ERROR); +} + +PR_IMPLEMENT(void) +_PR_MD_DESTROY_SEM(_MDSemaphore *md) +{ + int rv; + rv = DosCloseEventSem(md->sem); + PR_ASSERT(rv == NO_ERROR); + +} + +PR_IMPLEMENT(PRStatus) +_PR_MD_TIMED_WAIT_SEM(_MDSemaphore *md, PRIntervalTime ticks) +{ + int rv; + rv = DosWaitEventSem(md->sem, PR_IntervalToMilliseconds(ticks)); + + if (rv == NO_ERROR) + return PR_SUCCESS; + else + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_PR_MD_WAIT_SEM(_MDSemaphore *md) +{ + return _PR_MD_TIMED_WAIT_SEM(md, PR_INTERVAL_NO_TIMEOUT); +} + +PR_IMPLEMENT(void) +_PR_MD_POST_SEM(_MDSemaphore *md) +{ + int rv; + rv = DosPostEventSem(md->sem); + PR_ASSERT(rv == NO_ERROR); +} + + diff --git a/pr/src/md/os2/os2sock.c b/pr/src/md/os2/os2sock.c new file mode 100644 index 00000000..3eb26d35 --- /dev/null +++ b/pr/src/md/os2/os2sock.c @@ -0,0 +1,770 @@ +/* -*- 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. + */ + +/* OS/2 Sockets module + * + */ + +/*Note from DSR111297 - it should be noted that there are two flavors of select() on OS/2 */ +/*There is standard BSD (which is kind of slow) and a new flavor of select() that takes */ +/*an integer list of sockets, the number of read sockets, write sockets, except sockets, and */ +/*a millisecond count for timeout. In the interest of performance I have choosen the OS/2 */ +/*specific version of select(). See OS/2 TCP/IP Programmer's Toolkit for more info. */ + +#include "primpl.h" + +void +_PR_MD_INIT_IO() +{ + sock_init(); +} + +/* --- SOCKET IO --------------------------------------------------------- */ + + +PRInt32 +_PR_MD_SOCKET(int af, int type, int flags) +{ + int sock; + PRUint32 one = 1; + PRInt32 rv; + PRInt32 err; + + sock = socket(af, type, flags); + + if (sock == -1 ) + { + int rv = sock_errno(); + soclose(sock); + _PR_MD_MAP_SOCKET_ERROR(rv); + return (PRInt32) -1; + } + + /* + ** Make the socket Non-Blocking + */ + rv = ioctl( sock, FIONBIO, (char *) &one, sizeof(one)); + if ( rv != 0 ) + { + err = sock_errno(); + return -1; + } + + return (PRInt32)sock; +} + +/* +** _MD_CloseSocket() -- Close a socket +** +*/ +PRInt32 +_MD_CloseSocket(PRInt32 osfd) +{ + PRInt32 rv = -1; + + rv = soclose((int) osfd ); + if (rv < 0) + _PR_MD_MAP_SOCKET_ERROR(sock_errno()); + + return rv; +} + +PRInt32 +_MD_SocketAvailable(PRFileDesc *fd) +{ + PRInt32 result; + + if (ioctl(fd->secret->md.osfd, FIONREAD, (char *) &result, sizeof(result)) < 0) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, sock_errno()); + return -1; + } + return result; +} + +PRInt32 +_MD_Accept(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, + PRIntervalTime timeout ) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; +#ifdef BSD_SELECT + fd_set rd; + struct timeval tv, *tvp; + + FD_ZERO(&rd); + FD_SET(osfd, &rd); +#else + int socks[1]; + socks[0] = osfd; +#endif + if (timeout == PR_INTERVAL_NO_TIMEOUT) + { + while ((rv = accept(osfd, (struct sockaddr *) raddr, (int *) rlen)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + if ((rv = select(osfd + 1, &rd, NULL, NULL,NULL)) == -1) { +#else + if ((rv = select(socks, 1, 0, 0, -1)) == -1) { +#endif + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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, (int *) rlen)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!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, (int *) rlen)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + 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); +#else + long lTimeout = PR_IntervalToMilliseconds(timeout); + rv = select(socks, 1, 0, 0, lTimeout); +#endif + if (rv > 0) { + goto retry; + } + else if (rv == 0) + { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + } else { + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + } + } 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; +#ifdef BSD_SELECT + fd_set wd, ex; + struct timeval tv, *tvp; +#else + int socks[1]; + long lTimeout = -1; +#endif + + if ((rv = connect(osfd, (struct sockaddr *) addr, addrlen)) == -1) + { + err = sock_errno(); + if ((!fd->secret->nonblocking) && (err == EINPROGRESS) || (err == EWOULDBLOCK)) + { +#ifdef BSD_SELECT + 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(osfd, &wd); + FD_ZERO(&ex); + FD_SET(osfd, &ex); + rv = select(osfd + 1, NULL, &wd, &ex, tvp); +#else + if (timeout == PR_INTERVAL_NO_TIMEOUT) + lTimeout = -1; + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + + socks[0] = osfd; + rv = select(socks, 0, 1, 1, lTimeout); +#endif + if (rv > 0) + { +#ifdef BSD_SELECT + if (FD_ISSET(osfd, &ex)) + { + DosSleep(0); + len = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &len) < 0) + { + _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno()); + return -1; + } + if (err != 0) + _PR_MD_MAP_CONNECT_ERROR(err); + else + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + if (FD_ISSET(osfd, &wd)) + { + /* it's connected */ + return 0; + } +#else + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, + (char *) &err, &len) < 0) + { + _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno()); + return -1; + } + else + return 0; /* It's connected ! */ +#endif + } + else if (rv == 0) + { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + return(-1); + } else if (rv < 0) + { + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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, (struct sockaddr*) &(addr->inet), addrlen); + + if (rv == -1) { + _PR_MD_MAP_BIND_ERROR(sock_errno()); + 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; +#ifdef BSD_SELECT + struct timeval tv, *tvp; + fd_set rd; +#else + int socks[1]; + long lTimeout = -1; +#endif + + while ((rv = recv( osfd, buf, amount, 0)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + FD_ZERO(&rd); + FD_SET(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) +#else + socks[0] = osfd; + if (timeout == PR_INTERVAL_NO_TIMEOUT) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + if ((rv = select(socks, 1, 0, 0, lTimeout)) == -1) +#endif + { + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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; +#ifdef BSD_SELECT + struct timeval tv, *tvp; + fd_set wd; +#else + int socks[1]; + long lTimeout = -1; +#endif + PRInt32 bytesSent = 0; + + while(bytesSent < amount ) + { + while ((rv = send( osfd, (char *) buf, amount, 0 )) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + 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(osfd, &wd); + if ((rv = select( osfd + 1, NULL, &wd, NULL,tvp)) == -1) { +#else + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + socks[0] = osfd; + if ((rv = select( socks, 0, 1, 0, lTimeout)) == -1) { +#endif + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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 )) + { +#ifdef BSD_SELECT + 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(osfd, &wd); + if ((rv = select(osfd + 1, NULL, &wd, NULL,tvp)) == -1) { +#else + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + socks[0] = osfd; + if ((rv = select(socks, 0, 1, 0,lTimeout)) == -1) { +#endif + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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; + PRInt32 bytesSent = 0; +#ifdef BSD_SELECT + struct timeval tv, *tvp; + fd_set wd; +#else + int socks[1]; + long lTimeout = -1; +#endif + + while(bytesSent < amount) + { + while ((rv = sendto( osfd, (char *) buf, amount, 0, (struct sockaddr *) addr, + addrlen)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + 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(osfd, &wd); + if ((rv = select(osfd + 1, NULL, &wd, NULL, tvp)) == -1) { +#else + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + socks[0] = osfd; + if ((rv = select(socks, 0, 1, 0, lTimeout)) == -1) { +#endif + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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 )) + { +#ifdef BSD_SELECT + 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(osfd, &wd); + if ((rv = select( osfd + 1, NULL, &wd, NULL, tvp)) == -1) { +#else + if ( timeout == PR_INTERVAL_NO_TIMEOUT ) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + socks[0] = osfd; + if ((rv = select( socks, 0, 1, 0, lTimeout)) == -1) { +#endif + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + 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; + PRUint32 addrlen_temp = *addrlen; +#ifdef BSD_SELECT + struct timeval tv, *tvp; + fd_set rd; +#else + int socks[1]; + long lTimeout = -1; +#endif + + while ((rv = recvfrom( osfd, (char *) buf, amount, 0, (struct sockaddr *) addr, + (int *) addrlen)) == -1) + { + if (((err = sock_errno()) == EWOULDBLOCK) + && (!fd->secret->nonblocking)) + { +#ifdef BSD_SELECT + 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(osfd, &rd); + if ((rv = select(osfd + 1, &rd, NULL, NULL, tvp)) == -1) +#else + if (timeout == PR_INTERVAL_NO_TIMEOUT) + { + lTimeout = -1; + } + else + { + lTimeout = PR_IntervalToMilliseconds(timeout); + } + socks[0] = osfd; + if ((rv = select(socks, 1, 0, 0, lTimeout)) == -1) +#endif + { + _PR_MD_MAP_SELECT_ERROR(sock_errno()); + return -1; + } else if (rv == 0) + { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } + + /* recvfrom blows this value away if it fails first time */ + *addrlen = addrlen_temp; + } + 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(sock_errno()); + return rv; +} + +PRStatus +_PR_MD_GETSOCKNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + rv = getsockname((int)fd->secret->md.osfd, (struct sockaddr *)addr, (int *) len); + if (rv==0) + return PR_SUCCESS; + else { + _PR_MD_MAP_GETSOCKNAME_ERROR(sock_errno()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_GETPEERNAME(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *len) +{ + PRInt32 rv; + + rv = getpeername((int)fd->secret->md.osfd, (struct sockaddr *)addr, (int *) len); + if (rv==0) + return PR_SUCCESS; + else { + _PR_MD_MAP_GETPEERNAME_ERROR(sock_errno()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_GETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen) +{ + PRInt32 rv; + + rv = getsockopt((int)fd->secret->md.osfd, level, optname, optval, optlen); + if (rv==0) + return PR_SUCCESS; + else { + _PR_MD_MAP_GETSOCKOPT_ERROR(sock_errno()); + return PR_FAILURE; + } +} + +PRStatus +_PR_MD_SETSOCKOPT(PRFileDesc *fd, PRInt32 level, PRInt32 optname, const char* optval, PRInt32 optlen) +{ + PRInt32 rv; + + rv = setsockopt((int)fd->secret->md.osfd, level, optname, (char *) optval, optlen); + if (rv==0) + return PR_SUCCESS; + else { + _PR_MD_MAP_SETSOCKOPT_ERROR(sock_errno()); + return PR_FAILURE; + } +} + +void +_MD_MakeNonblock(PRFileDesc *f) +{ + return; /* do nothing! */ +} diff --git a/pr/src/md/os2/os2thred.c b/pr/src/md/os2/os2thred.c new file mode 100644 index 00000000..e51b3eb3 --- /dev/null +++ b/pr/src/md/os2/os2thred.c @@ -0,0 +1,245 @@ +/* -*- 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 _beginthread() */ + +APIRET (* APIENTRY QueryThreadContext)(TID, ULONG, PCONTEXTRECORD); + +/* --- globals ------------------------------------------------ */ +_NSPR_TLS* pThreadLocalStorage = 0; +_PRInterruptTable _pr_interruptTable[] = { { 0 } }; + +PR_IMPLEMENT(void) +_PR_MD_ENSURE_TLS() +{ + if(!pThreadLocalStorage) + { + /* Allocate thread local storage (TLS). Note, that only 32 bytes can + * be allocated at a time. + */ + int rc = DosAllocThreadLocalMemory(sizeof(_NSPR_TLS) / 4, (PULONG*)&pThreadLocalStorage); + PR_ASSERT(rc == NO_ERROR); + memset(pThreadLocalStorage, 0, sizeof(_NSPR_TLS)); + } +} + +PR_IMPLEMENT(void) +_PR_MD_EARLY_INIT() +{ + HMODULE hmod; + + if (DosLoadModule(NULL, 0, "DOSCALL1.DLL", &hmod) == 0) + DosQueryProcAddr(hmod, 877, "DOSQUERYTHREADCONTEXT", + (PFN *)&QueryThreadContext); +} + +PR_IMPLEMENT(void) +_PR_MD_INIT_PRIMORDIAL_THREAD(PRThread *thread) +{ + PTIB ptib; + PPIB ppib; + PRUword rc; + + rc = DosGetInfoBlocks(&ptib, &ppib); + + thread->md.handle = ptib->tib_ptib2->tib2_ultid; +} + + +PR_IMPLEMENT(PRStatus) +_PR_MD_INIT_THREAD(PRThread *thread) +{ + APIRET rc; + + if (thread->flags & _PR_PRIMORDIAL) + _PR_MD_INIT_PRIMORDIAL_THREAD(thread); + + /* Create the blocking IO semaphore */ + _PR_MD_NEW_SEM(&thread->md.blocked_sema, 1); + return (thread->md.blocked_sema.sem != NULL) ? PR_SUCCESS : PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_PR_MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + thread->md.handle = thread->id = (TID) _beginthread( + (void(* _Optlink)(void*))start, + NULL, + thread->stack->stackSize, + thread); + if(thread->md.handle == -1) { + return PR_FAILURE; + } + _PR_MD_SET_PRIORITY(&(thread->md), priority); + + return PR_SUCCESS; +} + +PR_IMPLEMENT(void) +_PR_MD_YIELD(void) +{ + /* Isn't there some problem with DosSleep(0) on OS/2? */ + DosSleep(0); +} + +PR_IMPLEMENT(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 = PRTYC_IDLETIME; + break; + case PR_PRIORITY_NORMAL: + nativePri = PRTYC_REGULAR; + break; + case PR_PRIORITY_HIGH: + nativePri = PRTYC_FOREGROUNDSERVER; + break; + case PR_PRIORITY_URGENT: + nativePri = PRTYC_TIMECRITICAL; + } + rv = DosSetPriority(PRTYS_THREAD, nativePri, 0, thread->handle); + PR_ASSERT(rv == NO_ERROR); + if (rv != NO_ERROR) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_SetThreadPriority: can't set thread priority\n")); + } + return; +} + +PR_IMPLEMENT(void) +_PR_MD_CLEAN_THREAD(PRThread *thread) +{ + /* Just call _PR_MD_EXIT_THREAD for now */ + _PR_MD_EXIT_THREAD(thread); +} + +PR_IMPLEMENT(void) +_PR_MD_EXIT_THREAD(PRThread *thread) +{ + _PR_MD_DESTROY_SEM(&thread->md.blocked_sema); + + if (thread->md.handle) { + /* DosKillThread will not kill a suspended thread, but it will mark it + * for death; we must resume it after killing it to make sure it knows + * it is about to die (pretty wicked, huh?). + * + * DosKillThread will not kill the current thread, instead we must use + * DosExit. + */ + if ( thread != _MD_CURRENT_THREAD() ) { + DosKillThread( thread->md.handle ); + DosResumeThread( thread->md.handle ); + } else { + _endthread(); + } + thread->md.handle = 0; + } + + _PR_MD_SET_CURRENT_THREAD(NULL); +} + + +PR_IMPLEMENT(void) +_PR_MD_EXIT(PRIntn status) +{ + _exit(status); +} + +#ifdef HAVE_THREAD_AFFINITY +PR_EXTERN(PRInt32) +_PR_MD_SETTHREADAFFINITYMASK(PRThread *thread, PRUint32 mask ) +{ + /* Can we do this on OS/2? Only on SMP versions? */ + PR_ASSERT(!"Not implemented"); + return 0; + + /* This is what windows does: + int rv; + + rv = SetThreadAffinityMask(thread->md.handle, mask); + + return rv?0:-1; + */ +} + +PR_EXTERN(PRInt32) +_PR_MD_GETTHREADAFFINITYMASK(PRThread *thread, PRUint32 *mask) +{ + /* Can we do this on OS/2? Only on SMP versions? */ + PR_ASSERT(!"Not implemented"); + return 0; + + /* This is what windows does: + PRInt32 rv, system_mask; + + rv = GetProcessAffinityMask(GetCurrentProcess(), mask, &system_mask); + + return rv?0:-1; + */ +} +#endif /* HAVE_THREAD_AFFINITY */ + +PR_IMPLEMENT(void) +_PR_MD_SUSPEND_CPU(_PRCPU *cpu) +{ + _PR_MD_SUSPEND_THREAD(cpu->thread); +} + +PR_IMPLEMENT(void) +_PR_MD_RESUME_CPU(_PRCPU *cpu) +{ + _PR_MD_RESUME_THREAD(cpu->thread); +} + +PR_IMPLEMENT(void) +_PR_MD_SUSPEND_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + APIRET rc; + + /* XXXMB - DosSuspendThread() is not a blocking call; how do we + * know when the thread is *REALLY* suspended? + */ + rc = DosSuspendThread(thread->md.handle); + PR_ASSERT(rc == NO_ERROR); + } +} + +PR_IMPLEMENT(void) +_PR_MD_RESUME_THREAD(PRThread *thread) +{ + if (_PR_IS_NATIVE_THREAD(thread)) { + DosResumeThread(thread->md.handle); + } +} + diff --git a/pr/src/md/prosdep.c b/pr/src/md/prosdep.c new file mode 100644 index 00000000..fc2d35cb --- /dev/null +++ b/pr/src/md/prosdep.c @@ -0,0 +1,87 @@ +/* -*- 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 "prbit.h" +#include "prsystem.h" + +#if defined(XP_MAC) +#include "prosdep.h" +#else +#include "md/prosdep.h" +#endif + +#ifdef XP_UNIX +#include <unistd.h> +#endif + +PRInt32 _pr_pageShift; +PRInt32 _pr_pageSize; + +/* +** Get system page size +*/ +static void GetPageSize(void) +{ + PRInt32 pageSize; + + /* Get page size */ +#ifdef XP_UNIX +#if defined SUNOS4 || defined LINUX || defined BSDI || defined AIX \ + || defined FREEBSD + _pr_pageSize = getpagesize(); +#elif defined(HPUX) + /* I have no idea. Don't get me started. --Rob */ + _pr_pageSize = sysconf(_SC_PAGE_SIZE); +#else + _pr_pageSize = sysconf(_SC_PAGESIZE); +#endif +#endif /* XP_UNIX */ + +#ifdef XP_MAC + _pr_pageSize = 4096; +#endif /* XP_MAC */ + +#ifdef XP_PC +#ifdef _WIN32 + SYSTEM_INFO info; + GetSystemInfo(&info); + _pr_pageSize = info.dwPageSize; +#else + _pr_pageSize = 4096; +#endif +#endif /* XP_PC */ + + pageSize = _pr_pageSize; + PR_CEILING_LOG2(_pr_pageShift, pageSize); +} + +PR_IMPLEMENT(PRInt32) PR_GetPageShift(void) +{ + if (!_pr_pageSize) { + GetPageSize(); + } + return _pr_pageShift; +} + +PR_IMPLEMENT(PRInt32) PR_GetPageSize(void) +{ + if (!_pr_pageSize) { + GetPageSize(); + } + return _pr_pageSize; +} diff --git a/pr/src/md/unix/Makefile b/pr/src/md/unix/Makefile new file mode 100644 index 00000000..2c3a3f77 --- /dev/null +++ b/pr/src/md/unix/Makefile @@ -0,0 +1,229 @@ +# +# 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. +# + +MOD_DEPTH = ../../../.. + +include $(MOD_DEPTH)/config/config.mk + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + unix.c \ + unix_errors.c \ + uxproces.c \ + uxwrap.c \ + $(NULL) + +PTH_USER_CSRCS = \ + pthreads_user.c \ + $(NULL) + +IRIX_CSRCS = \ + irix.c \ + $(NULL) + +SUNOS4_CSRCS = \ + sunos4.c \ + $(NULL) + +SOLARIS_CSRCS = \ + solaris.c \ + $(NULL) + +AIX_CSRCS = \ + aix.c \ + $(NULL) + +FREEBSD_CSRCS = \ + freebsd.c \ + $(NULL) + +BSDI_CSRCS = \ + bsdi.c \ + $(NULL) + +HPUX_CSRCS = \ + hpux.c \ + $(NULL) + +OSF1_CSRCS = \ + osf1.c \ + $(NULL) + +LINUX_CSRCS = \ + linux.c \ + $(NULL) + +UNIXWARE_CSRCS = \ + unixware.c \ + $(NULL) + +RELIANTUNIX_CSRCS = \ + reliantunix.c \ + $(NULL) + +NEC_CSRCS = \ + nec.c \ + $(NULL) + +SONY_CSRCS = \ + sony.c \ + $(NULL) + +NCR_CSRCS = \ + ncr.c \ + $(NULL) + +SCOOS_CSRCS = \ + scoos.c \ + $(NULL) + +ifeq ($(PTHREADS_USER),1) +CSRCS += $(PTH_USER_CSRCS) +endif + +ifeq ($(OS_ARCH),IRIX) +CSRCS += $(IRIX_CSRCS) +endif + +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +CSRCS += $(SUNOS4_CSRCS) +else +CSRCS += $(SOLARIS_CSRCS) +endif +endif + +ifeq ($(OS_ARCH),AIX) +CSRCS += $(AIX_CSRCS) +endif +ifeq ($(OS_ARCH),FreeBSD) +CSRCS += $(FREEBSD_CSRCS) +endif +ifeq ($(OS_ARCH),BSD_386) +CSRCS += $(BSDI_CSRCS) +endif +ifeq ($(OS_ARCH),HP-UX) +CSRCS += $(HPUX_CSRCS) +endif +ifeq ($(OS_ARCH),OSF1) +CSRCS += $(OSF1_CSRCS) +endif +ifeq ($(OS_ARCH),Linux) +CSRCS += $(LINUX_CSRCS) +endif +ifeq ($(OS_ARCH),UNIXWARE) +CSRCS += $(UNIXWARE_CSRCS) +endif +ifeq ($(OS_ARCH),ReliantUNIX) +CSRCS += $(RELIANTUNIX_CSRCS) +endif +ifeq ($(OS_ARCH),NEC) +CSRCS += $(NEC_CSRCS) +endif +ifeq ($(OS_ARCH),NEWS-OS) +CSRCS += $(SONY_CSRCS) +endif +ifeq ($(OS_ARCH),NCR) +CSRCS += $(NCR_CSRCS) +endif +ifeq ($(OS_ARCH),SCO_SV) +CSRCS += $(SCOOS_CSRCS) +endif + +# +# Some Unix platforms have an assembly language file. +# E.g., AIX 3.2, Solaris (both sparc and x86). +# +ifeq ($(OS_ARCH), AIX) + ifeq ($(OS_RELEASE), 3.2) + ASFILES = os_$(OS_ARCH).s + endif +endif + +ifeq ($(OS_ARCH),SunOS) + ifeq ($(OS_TEST),i86pc) + ASFILES = os_$(OS_ARCH)_x86.s + else + ifneq ($(OS_RELEASE),4.1.3_U1) + ASFILES = os_$(OS_ARCH).s + endif + endif +endif + +ifeq ($(OS_ARCH), ReliantUNIX) + ASFILES = os_$(OS_ARCH).s +endif + +ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_3862.1) + ASFILES = os_BSD_386_2.s +endif + +TARGETS = $(OBJS) + +ifeq ($(OS_ARCH),AIX) +ifneq ($(OS_RELEASE),4.2) +ifneq ($(USE_PTHREADS), 1) +#TARGETS += $(OBJDIR)/aixwrap.o +endif +endif +endif + +ifeq ($(OS_ARCH),SunOS) + ifneq ($(OS_RELEASE),4.1.3_U1) + ifeq ($(OS_TEST),sun4u) + LIBRARY_NAME = $(ULTRASPARC_LIBRARY) + LIBRARY_VERSION = $(MOD_VERSION) + ULTRASPARC_ASFILES = os_$(OS_ARCH)_ultrasparc.s + ULTRASPARC_ASOBJS = $(addprefix $(OBJDIR)/,$(ULTRASPARC_ASFILES:.s=.o)) + TARGETS += $(ULTRASPARC_ASOBJS) $(SHARED_LIBRARY) + endif + endif +endif + +INCLUDES = -I$(DIST)/include/private -I$(DIST)/include + +include $(MOD_DEPTH)/config/rules.mk + +export:: $(TARGETS) +#ifeq ($(OS_ARCH),AIX) +#ifneq ($(OS_RELEASE),4.2) +#ifneq ($(USE_PTHREADS), 1) +# $(INSTALL) -m 444 $(OBJDIR)/aixwrap.o $(DIST)/lib +#endif +#endif +#endif +ifeq ($(OS_ARCH),SunOS) +ifneq ($(OS_RELEASE),4.1.3_U1) +ifeq ($(OS_TEST),sun4u) +$(SHARED_LIBRARY): $(ULTRASPARC_ASOBJS) + $(LD) -G -z text -o $@ $(ULTRASPARC_ASOBJS) + $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/lib + +$(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES) + as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $(ULTRASPARC_ASFILES) +endif +endif +endif + + +install:: export diff --git a/pr/src/md/unix/aix.c b/pr/src/md/unix/aix.c new file mode 100644 index 00000000..8ae7e303 --- /dev/null +++ b/pr/src/md/unix/aix.c @@ -0,0 +1,165 @@ +/* -*- 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" + +/* + * NSPR 2.0 overrides the system select() and poll() functions. + * On AIX 4.2, we use dlopen("/unix", 0) and dlsym() to get to the + * original system select() and poll() functions. + */ + +#ifndef AIX4_1 + +#include <sys/select.h> +#include <sys/poll.h> +#include <dlfcn.h> + +static void *aix_handle = NULL; +static int (*aix_select_fcn)() = NULL; +static int (*aix_poll_fcn)() = NULL; + +int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) +{ + int rv; + + if (!aix_select_fcn) { + if (!aix_handle) { + aix_handle = dlopen("/unix",0); + if (!aix_handle) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + aix_select_fcn = (int(*)())dlsym(aix_handle,"select"); + if (!aix_select_fcn) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + rv = (*aix_select_fcn)(width, r, w, e, t); + return rv; +} + +int _MD_POLL(void *listptr, unsigned long nfds, long timeout) +{ + int rv; + + if (!aix_poll_fcn) { + if (!aix_handle) { + aix_handle = dlopen("/unix",0); + if (!aix_handle) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + aix_poll_fcn = (int(*)())dlsym(aix_handle,"poll"); + if (!aix_poll_fcn) { + PR_SetError(PR_UNKNOWN_ERROR, 0); + return -1; + } + } + rv = (*aix_poll_fcn)(listptr, nfds, timeout); + return rv; +} + +#else + +/* + * In AIX versions prior to 4.2, we use the two-step rename/link trick. + * The binary must contain at least one "poll" symbol for linker's rename + * to work. So we must have this dummy function that references poll(). + */ +#include <sys/poll.h> +void _pr_aix_dummy() +{ + poll(0,0,0); +} + +#endif /* !AIX4_1 */ + +#if !defined(PTHREADS_USER) + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +PR_IMPLEMENT(void) +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PR_IMPLEMENT(PRStatus) +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for AIX */ +PR_IMPLEMENT(void) +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for AIX."); +} + +PR_IMPLEMENT(PRStatus) +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for AIX."); +} +#endif /* _PR_PTHREADS */ +#endif /* PTHREADS_USER */ diff --git a/pr/src/md/unix/aixwrap.c b/pr/src/md/unix/aixwrap.c new file mode 100644 index 00000000..e5c0a630 --- /dev/null +++ b/pr/src/md/unix/aixwrap.c @@ -0,0 +1,46 @@ +/* -*- 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. + */ + +/* + * File: aixwrap.c + * Description: + * This file contains a single function, _MD_SELECT(), which simply + * invokes the select() function. This file is used in an ugly + * hack to override the system select() function on AIX releases + * prior to 4.2. (On AIX 4.2, we use a different mechanism to + * override select().) + */ + +#ifndef AIX4_1 +#error aixwrap.c should only be used on AIX 4.1 +#else + +#include <sys/select.h> +#include <sys/poll.h> + +int _MD_SELECT(int width, fd_set *r, fd_set *w, fd_set *e, struct timeval *t) +{ + return select(width, r, w, e, t); +} + +int _MD_POLL(void *listptr, unsigned long nfds, long timeout) +{ + return poll(listptr, nfds, timeout); +} + +#endif /* AIX4_1 */ diff --git a/pr/src/md/unix/bsdi.c b/pr/src/md/unix/bsdi.c new file mode 100644 index 00000000..dae92a47 --- /dev/null +++ b/pr/src/md/unix/bsdi.c @@ -0,0 +1,93 @@ +/* -*- 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 <signal.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for BSDI */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for BSDI."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for BSDI."); + return PR_FAILURE; +} diff --git a/pr/src/md/unix/freebsd.c b/pr/src/md/unix/freebsd.c new file mode 100644 index 00000000..2cedf308 --- /dev/null +++ b/pr/src/md/unix/freebsd.c @@ -0,0 +1,232 @@ +/* -*- 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 <signal.h> + +void _MD_EarlyInit(void) +{ + /* + * Ignore FPE because coercion of a NaN to an int causes SIGFPE + * to be raised. + */ + struct sigaction act; + + act.sa_handler = SIG_IGN; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + sigaction(SIGFPE, &act, 0); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) sigsetjmp(CONTEXT(t), 1); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for OSF1 */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for OSF1."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ + +#if defined(_PR_NEED_FAKE_POLL) + +#include <fcntl.h> + +int poll(struct pollfd *filedes, unsigned long nfds, int timeout) +{ + int i; + int rv; + int maxfd; + fd_set rd, wr, ex; + struct timeval tv, *tvp; + + if (timeout < 0 && timeout != -1) { + errno = EINVAL; + return -1; + } + + if (timeout == -1) { + tvp = NULL; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + tvp = &tv; + } + + maxfd = -1; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + for (i = 0; i < nfds; i++) { + int osfd = filedes[i].fd; + int events = filedes[i].events; + PRBool fdHasEvent = PR_FALSE; + + if (osfd < 0) { + continue; /* Skip this osfd. */ + } + + /* + * Map the native poll flags to nspr poll flags. + * POLLIN, POLLRDNORM ===> PR_POLL_READ + * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE + * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPTION + * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) + * are ignored. + * + * The output events POLLERR and POLLHUP are never turned on. + * POLLNVAL may be turned on. + */ + + if (events & (POLLIN | POLLRDNORM)) { + FD_SET(osfd, &rd); + fdHasEvent = PR_TRUE; + } + if (events & (POLLOUT | POLLWRNORM)) { + FD_SET(osfd, &wr); + fdHasEvent = PR_TRUE; + } + if (events & (POLLPRI | POLLRDBAND)) { + FD_SET(osfd, &ex); + fdHasEvent = PR_TRUE; + } + if (fdHasEvent && osfd > maxfd) { + maxfd = osfd; + } + } + + rv = select(maxfd + 1, &rd, &wr, &ex, tvp); + + /* Compute poll results */ + if (rv > 0) { + rv = 0; + for (i = 0; i < nfds; i++) { + PRBool fdHasEvent = PR_FALSE; + + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (FD_ISSET(filedes[i].fd, &rd)) { + if (filedes[i].events & POLLIN) { + filedes[i].revents |= POLLIN; + } + if (filedes[i].events & POLLRDNORM) { + filedes[i].revents |= POLLRDNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &wr)) { + if (filedes[i].events & POLLOUT) { + filedes[i].revents |= POLLOUT; + } + if (filedes[i].events & POLLWRNORM) { + filedes[i].revents |= POLLWRNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &ex)) { + if (filedes[i].events & POLLPRI) { + filedes[i].revents |= POLLPRI; + } + if (filedes[i].events & POLLRDBAND) { + filedes[i].revents |= POLLRDBAND; + } + fdHasEvent = PR_TRUE; + } + if (fdHasEvent) { + rv++; + } + } + PR_ASSERT(rv > 0); + } else if (rv == -1 && errno == EBADF) { + rv = 0; + for (i = 0; i < nfds; i++) { + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) { + filedes[i].revents = POLLNVAL; + rv++; + } + } + PR_ASSERT(rv > 0); + } + PR_ASSERT(-1 != timeout || rv != 0); + + return rv; +} +#endif /* _PR_NEED_FAKE_POLL */ diff --git a/pr/src/md/unix/hpux.c b/pr/src/md/unix/hpux.c new file mode 100644 index 00000000..701a854b --- /dev/null +++ b/pr/src/md/unix/hpux.c @@ -0,0 +1,334 @@ +/* -*- 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" +#if defined(HPUX10_30) || defined(HPUX11) +/* for fesettrapenable */ +#include <fenv.h> +#else +/* for fpsetmask */ +#include <math.h> +#endif +#include <setjmp.h> +#include <signal.h> +#include <values.h> + +/* +** On HP-UX we need to define a SIGFPE handler because coercion of a +** NaN to an int causes SIGFPE to be raised. Thanks to Marianne +** Mueller and Doug Priest at SunSoft for this fix. +** +** Under DCE threads, sigaction() installs a per-thread signal handler, +** so we use the sigvector() interface to install a process-wide +** handler. +*/ + +#ifdef _PR_DCETHREADS +static void +CatchFPE(int sig, int code, struct sigcontext *scp) +{ + unsigned i, e; + int r, t; + int *source, *destination; + + /* check excepting instructions */ + for ( i = 0; i < 7; i++ ) { + e = *(i+&(scp->sc_sl.sl_ss.ss_frexcp1)); + if ( e & 0xfc000000 != 0 ) { + if ((e & 0xf4017720) == 0x24010200) { + r = ((e >> 20) & 0x3e); + t = (e & 0x1f) << 1; + if (e & 0x08000000) { + r |= (e >> 7) & 1; + t |= (e >> 6) & 1; + } + source = (int *)(&scp->sc_sl.sl_ss.ss_frstat + r); + destination = (int *)(&scp->sc_sl.sl_ss.ss_frstat + t); + *destination = *source < 0 ? -MAXINT-1 : MAXINT; + } + } + *(i+&(scp->sc_sl.sl_ss.ss_frexcp1)) = 0; + } + + /* clear T-bit */ + scp->sc_sl.sl_ss.ss_frstat &= ~0x40; +} +#else /* _PR_DCETHREADS */ +static void +CatchFPE(int sig, siginfo_t *info, void *context) +{ + ucontext_t *ucp = (ucontext_t *) context; + unsigned i, e; + int r, t; + int *source, *destination; + + /* check excepting instructions */ + for ( i = 0; i < 7; i++ ) { + e = *(i+&(ucp->uc_mcontext.ss_frexcp1)); + if ( e & 0xfc000000 != 0 ) { + if ((e & 0xf4017720) == 0x24010200) { + r = ((e >> 20) & 0x3e); + t = (e & 0x1f) << 1; + if (e & 0x08000000) { + r |= (e >> 7) & 1; + t |= (e >> 6) & 1; + } + source = (int *)(&ucp->uc_mcontext.ss_frstat + r); + destination = (int *)(&ucp->uc_mcontext.ss_frstat + t); + *destination = *source < 0 ? -MAXINT-1 : MAXINT; + } + } + *(i+&(ucp->uc_mcontext.ss_frexcp1)) = 0; + } + + /* clear T-bit */ + ucp->uc_mcontext.ss_frstat &= ~0x40; +} +#endif /* _PR_DCETHREADS */ + +void _MD_hpux_install_sigfpe_handler(void) +{ +#ifdef _PR_DCETHREADS + struct sigvec v; + + v.sv_handler = CatchFPE; + v.sv_mask = 0; + v.sv_flags = 0; + sigvector(SIGFPE, &v, NULL); +#else + struct sigaction act; + + sigaction(SIGFPE, NULL, &act); + act.sa_flags |= SA_SIGINFO; + act.sa_sigaction = CatchFPE; + sigaction(SIGFPE, &act, NULL); +#endif /* _PR_DCETHREADS */ + +#if defined(HPUX10_30) || defined(HPUX11) + fesettrapenable(FE_INVALID); +#else + fpsetmask(FP_X_INV); +#endif +} + +#if !defined(PTHREADS_USER) + +void _MD_EarlyInit(void) +{ + _MD_hpux_install_sigfpe_handler(); + +#ifndef _PR_PTHREADS + /* + * The following piece of code is taken from ns/nspr/src/md_HP-UX.c. + * In the comment for revision 1.6, dated 1995/09/11 23:33:34, + * robm says: + * This version has some problems which need to be addressed. + * First, intercept all system calls and prevent them from + * executing the library code which performs stack switches + * before normal system call invocation. In order for library + * calls which make system calls to work (like stdio), however, + * we must also allocate our own stack and switch the primordial + * stack to use it. This isn't so bad, except that I fudged the + * backtrace length when copying the old stack to the new one. + * + * This is the original comment of robm in the code: + * XXXrobm Horrific. To avoid a problem with HP's system call + * code, we allocate a new stack for the primordial thread and + * use it. However, we don't know how far back the original stack + * goes. We should create a routine that performs a backtrace and + * finds out just how much we need to copy. As a temporary measure, + * I just copy an arbitrary guess. + * + * In an email to servereng dated 2 Jan 1997, Mike Patnode (mikep) + * suggests that this only needs to be done for HP-UX 9. + */ +#ifdef HPUX9 +#define PIDOOMA_STACK_SIZE 524288 +#define BACKTRACE_SIZE 8192 + { + jmp_buf jb; + char *newstack; + char *oldstack; + + if(!setjmp(jb)) { + newstack = (char *) PR_MALLOC(PIDOOMA_STACK_SIZE); + oldstack = (char *) (*(((int *) jb) + 1) - BACKTRACE_SIZE); + memcpy(newstack, oldstack, BACKTRACE_SIZE); + *(((int *) jb) + 1) = (int) (newstack + BACKTRACE_SIZE); + longjmp(jb, 1); + } + } +#endif /* HPUX9 */ +#endif /* !_PR_PTHREADS */ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for HP-UX */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for HP-UX."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for HP-UX."); +} +#endif /* _PR_PTHREADS */ + +void +_MD_suspend_thread(PRThread *thread) +{ +#ifdef _PR_PTHREADS +#endif +} + +void +_MD_resume_thread(PRThread *thread) +{ +#ifdef _PR_PTHREADS +#endif +} +#endif /* PTHREADS_USER */ + +/* + * See if we have the privilege to set the scheduling policy and + * priority of threads. Returns 0 if privilege is available. + * Returns EPERM otherwise. + */ + +#if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) +PRIntn pt_hpux_privcheck() +{ + PRIntn policy; + struct sched_param schedule; + PRIntn rv; + pthread_t me = pthread_self(); + + rv = pthread_getschedparam(me, &policy, &schedule); + PR_ASSERT(0 == rv); + rv = pthread_setschedparam(me, policy, &schedule); + PR_ASSERT(0 == rv || EPERM == rv); + return rv; +} +#endif + +/* + * The HP version of strchr is buggy. It looks past the end of the + * string and causes a segmentation fault when our (NSPR) version + * of malloc is used. + * + * A better solution might be to put a cushion in our malloc just in + * case HP's version of strchr somehow gets used instead of this one. + */ +char * +strchr(const char *s, int c) +{ + char ch; + + if (!s) { + return NULL; + } + + ch = (char) c; + + while ((*s) && ((*s) != ch)) { + s++; + } + + if ((*s) == ch) { + return (char *) s; + } + + return NULL; +} + +/* + * Implemementation of memcmp in HP-UX (verified on releases A.09.03, + * A.09.07, and B.10.10) dumps core if called with: + * 1. First operand with address = 1(mod 4). + * 2. Size = 1(mod 4) + * 3. Last byte of the second operand is the last byte of the page and + * next page is not accessible(not mapped or protected) + * Thus, using the following naive version (tons of optimizations are + * possible;^) + */ + +int memcmp(const void *s1, const void *s2, size_t n) +{ + register unsigned char *p1 = (unsigned char *) s1, + *p2 = (unsigned char *) s2; + + while (n-- > 0) { + register int r = ((int) ((unsigned int) *p1)) + - ((int) ((unsigned int) *p2)); + if (r) return r; + p1++; p2++; + } + return 0; +} diff --git a/pr/src/md/unix/irix.c b/pr/src/md/unix/irix.c new file mode 100644 index 00000000..58ebe922 --- /dev/null +++ b/pr/src/md/unix/irix.c @@ -0,0 +1,1308 @@ +/* -*- 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 <signal.h> + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/syssgi.h> +#include <sys/time.h> +#include <sys/immu.h> +#include <sys/utsname.h> +#include <sys/sysmp.h> +#include <sys/pda.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <sys/procfs.h> +#include <task.h> + +static void _MD_IntervalInit(void); + +#if defined(_PR_PTHREADS) +#else /* defined(_PR_PTHREADS) */ + +char *_nspr_sproc_private; /* ptr. to private region in every sproc */ + +extern PRUintn _pr_numCPU; + +typedef struct nspr_arena { + PRCList links; + usptr_t *usarena; +} nspr_arena; + +#define ARENA_PTR(qp) \ + ((nspr_arena *) ((char*) (qp) - offsetof(nspr_arena , links))) + +static usptr_t *alloc_new_arena(void); + +PRCList arena_list = PR_INIT_STATIC_CLIST(&arena_list); +ulock_t arena_list_lock; +nspr_arena first_arena; +int _nspr_irix_arena_cnt = 1; + +long _nspr_irix_lock_cnt = 0; +long _nspr_irix_sem_cnt = 0; +long _nspr_irix_pollsem_cnt = 0; + +usptr_t *_pr_usArena; +ulock_t _pr_heapLock; + +usema_t *_pr_irix_exit_sem; +PRInt32 _pr_irix_exit_now = 0; + + +#define _NSPR_DEF_INITUSERS 100 /* default value of CONF_INITUSERS */ +#define _NSPR_DEF_INITSIZE (4 * 1024 * 1024) /* 4 MB */ + +int _irix_initusers = _NSPR_DEF_INITUSERS; +int _irix_initsize = _NSPR_DEF_INITSIZE; + +PRIntn _pr_io_in_progress, _pr_clock_in_progress; + +PRInt32 _pr_md_irix_sprocs_created, _pr_md_irix_sprocs_failed; +PRInt32 _pr_md_irix_sprocs = 1; +PRCList _pr_md_irix_sproc_list = +PR_INIT_STATIC_CLIST(&_pr_md_irix_sproc_list); + +sigset_t ints_off; +extern sigset_t timer_set; + +#if !defined(PR_SETABORTSIG) +#define PR_SETABORTSIG 18 +#endif +/* + * terminate the entire application if any sproc exits abnormally + */ +PRBool _nspr_terminate_on_error = PR_TRUE; + +/* + * exported interface to set the shared arena parameters + */ +void _PR_Irix_Set_Arena_Params(PRInt32 initusers, PRInt32 initsize) +{ + _irix_initusers = initusers; + _irix_initsize = initsize; +} + +static usptr_t *alloc_new_arena() +{ + return(usinit("/dev/zero")); +} + +static PRStatus new_poll_sem(struct _MDThread *mdthr, int val) +{ +PRIntn _is; +PRStatus rv = PR_SUCCESS; +usema_t *sem = NULL; +PRCList *qp; +nspr_arena *arena; +usptr_t *irix_arena; +PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + _PR_LOCK(arena_list_lock); + for (qp = arena_list.next; qp != &arena_list; qp = qp->next) { + arena = ARENA_PTR(qp); + sem = usnewpollsema(arena->usarena, val); + if (sem != NULL) { + mdthr->cvar_pollsem = sem; + mdthr->pollsem_arena = arena->usarena; + break; + } + } + if (sem == NULL) { + /* + * If no space left in the arena allocate a new one. + */ + if (errno == ENOMEM) { + arena = PR_NEWZAP(nspr_arena); + if (arena != NULL) { + irix_arena = alloc_new_arena(); + if (irix_arena) { + PR_APPEND_LINK(&arena->links, &arena_list); + _nspr_irix_arena_cnt++; + arena->usarena = irix_arena; + sem = usnewpollsema(arena->usarena, val); + if (sem != NULL) { + mdthr->cvar_pollsem = sem; + mdthr->pollsem_arena = arena->usarena; + } else + rv = PR_FAILURE; + } else { + PR_DELETE(arena); + rv = PR_FAILURE; + } + + } else + rv = PR_FAILURE; + } else + rv = PR_FAILURE; + } + _PR_UNLOCK(arena_list_lock); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + if (rv == PR_SUCCESS) + _MD_ATOMIC_INCREMENT(&_nspr_irix_pollsem_cnt); + return rv; +} + +static void free_poll_sem(struct _MDThread *mdthr) +{ +PRIntn _is; +PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + usfreepollsema(mdthr->cvar_pollsem, mdthr->pollsem_arena); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + _MD_ATOMIC_DECREMENT(&_nspr_irix_pollsem_cnt); +} + +static PRStatus new_lock(struct _MDLock *lockp) +{ +PRIntn _is; +PRStatus rv = PR_SUCCESS; +ulock_t lock = NULL; +PRCList *qp; +nspr_arena *arena; +usptr_t *irix_arena; +PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + _PR_LOCK(arena_list_lock); + for (qp = arena_list.next; qp != &arena_list; qp = qp->next) { + arena = ARENA_PTR(qp); + lock = usnewlock(arena->usarena); + if (lock != NULL) { + lockp->lock = lock; + lockp->arena = arena->usarena; + break; + } + } + if (lock == NULL) { + /* + * If no space left in the arena allocate a new one. + */ + if (errno == ENOMEM) { + arena = PR_NEWZAP(nspr_arena); + if (arena != NULL) { + irix_arena = alloc_new_arena(); + if (irix_arena) { + PR_APPEND_LINK(&arena->links, &arena_list); + _nspr_irix_arena_cnt++; + arena->usarena = irix_arena; + lock = usnewlock(irix_arena); + if (lock != NULL) { + lockp->lock = lock; + lockp->arena = arena->usarena; + } else + rv = PR_FAILURE; + } else { + PR_DELETE(arena); + rv = PR_FAILURE; + } + + } else + rv = PR_FAILURE; + } else + rv = PR_FAILURE; + } + _PR_UNLOCK(arena_list_lock); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + if (rv == PR_SUCCESS) + _MD_ATOMIC_INCREMENT(&_nspr_irix_lock_cnt); + return rv; +} + +static void free_lock(struct _MDLock *lockp) +{ +PRIntn _is; +PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + usfreelock(lockp->lock, lockp->arena); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); + _MD_ATOMIC_DECREMENT(&_nspr_irix_lock_cnt); +} + +void _MD_FREE_LOCK(struct _MDLock *lockp) +{ + PRIntn _is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + free_lock(lockp); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); +} + +/* + * _MD_get_attached_thread + * Return the thread pointer of the current thread if it is attached. + * + * This function is needed for Irix because the thread-local-storage is + * implemented by mmapin'g a page with the MAP_LOCAL flag. This causes the + * sproc-private page to inherit contents of the page of the caller of sproc(). + */ +PRThread *_MD_get_attached_thread(void) +{ + if (_MD_GET_SPROC_PID() == getpid()) + return _PR_MD_CURRENT_THREAD(); + else + return 0; +} + +PRStatus _MD_NEW_LOCK(struct _MDLock *lockp) +{ + PRStatus rv; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + rv = new_lock(lockp); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return rv; +} + +static void +sigchld_handler(int sig) +{ + pid_t pid; + int status; + + /* + * If an sproc exited abnormally send a SIGKILL signal to all the + * sprocs in the process to terminate the application + */ + while ((pid = waitpid(0, &status, WNOHANG)) > 0) { + if (WIFSIGNALED(status) && ((WTERMSIG(status) == SIGSEGV) || + (WTERMSIG(status) == SIGBUS) || + (WTERMSIG(status) == SIGABRT) || + (WTERMSIG(status) == SIGILL))) + + prctl(PR_SETEXITSIG, SIGKILL); + exit(status); + } +} + +static void save_context_and_block(int sig) +{ +PRThread *me = _PR_MD_CURRENT_THREAD(); +_PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + /* + * save context + */ + (void) setjmp(me->md.jb); + /* + * unblock the suspending thread + */ + if (me->cpu) { + /* + * I am a cpu thread, not a user-created GLOBAL thread + */ + unblockproc(cpu->md.suspending_id); + } else { + unblockproc(me->md.suspending_id); + } + /* + * now, block current thread + */ + blockproc(getpid()); +} + +/* +** The irix kernel has a bug in it which causes async connect's which are +** interrupted by a signal to fail terribly (EADDRINUSE is returned). +** We work around the bug by blocking signals during the async connect +** attempt. +*/ +PRInt32 _MD_irix_connect( + PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout) +{ + PRInt32 rv; + sigset_t oldset; + + sigprocmask(SIG_BLOCK, &ints_off, &oldset); + rv = connect(osfd, addr, addrlen); + sigprocmask(SIG_SETMASK, &oldset, 0); + + return(rv); +} + +#include "prprf.h" + +/********************************************************************/ +/********************************************************************/ +/*************** Various thread like things for IRIX ****************/ +/********************************************************************/ +/********************************************************************/ + +void *_MD_GetSP(PRThread *t) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + void *sp; + + if (me == t) + (void) setjmp(t->md.jb); + + sp = (void *)(t->md.jb[JB_SP]); + PR_ASSERT((sp >= (void *) t->stack->stackBottom) && + (sp <= (void *) (t->stack->stackBottom + t->stack->stackSize))); + return(sp); +} + +void _MD_InitLocks() +{ + char buf[200]; + char *init_users, *init_size; + + PR_snprintf(buf, sizeof(buf), "/dev/zero"); + + if (init_users = getenv("_NSPR_IRIX_INITUSERS")) + _irix_initusers = atoi(init_users); + + if (init_size = getenv("_NSPR_IRIX_INITSIZE")) + _irix_initsize = atoi(init_size); + + usconfig(CONF_INITUSERS, _irix_initusers); + usconfig(CONF_INITSIZE, _irix_initsize); + usconfig(CONF_AUTOGROW, 1); + usconfig(CONF_AUTORESV, 1); + if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0) { + perror("PR_Init: unable to config mutex arena"); + exit(-1); + } + + _pr_usArena = usinit(buf); + if (!_pr_usArena) { + fprintf(stderr, + "PR_Init: Error - unable to create lock/monitor arena\n"); + exit(-1); + } + _pr_heapLock = usnewlock(_pr_usArena); + + arena_list_lock = usnewlock(_pr_usArena); + _nspr_irix_lock_cnt = 3; + + _pr_irix_exit_sem = usnewsema(_pr_usArena, 0); + _nspr_irix_sem_cnt = 1; + + first_arena.usarena = _pr_usArena; + PR_INIT_CLIST(&first_arena.links); + PR_APPEND_LINK(&first_arena.links, &arena_list); +} + +/* _PR_IRIX_CHILD_PROCESS is a private API for Server group */ +void _PR_IRIX_CHILD_PROCESS() +{ + PR_ASSERT(_PR_MD_CURRENT_CPU() == _pr_primordialCPU); + PR_ASSERT(_pr_numCPU == 1); + PR_ASSERT((_pr_userActive + _pr_systemActive) == 2); + /* + * save the new pid + */ + _pr_primordialCPU->md.id = getpid(); +} + +static PRStatus pr_cvar_wait_sem(PRThread *thread, PRIntervalTime timeout) +{ + struct timeval tv, *tvp; + fd_set rd; + int rv; + + 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(thread->md.cvar_pollsemfd, &rd); + /* + * call uspsema only if a previous select call on this semaphore + * did not timeout + */ + if (!thread->md.cvar_pollsem_select) { + rv = _PR_WAIT_SEM(thread->md.cvar_pollsem); + PR_ASSERT(rv >= 0); + } else + rv = 0; +again: + if(!rv) { + rv = _MD_SELECT(thread->md.cvar_pollsemfd + 1, &rd, + NULL,NULL,tvp); + if ((rv == -1) && (errno == EINTR)) { + rv = 0; + goto again; + } + PR_ASSERT(rv >= 0); + } + + if (rv > 0) { + /* + * acquired the semaphore, call uspsema next time + */ + thread->md.cvar_pollsem_select = 0; + return PR_SUCCESS; + } else { + /* + * select timed out; must call select, not uspsema, when trying + * to acquire the semaphore the next time + */ + thread->md.cvar_pollsem_select = 1; + return PR_FAILURE; + } +} + +PRStatus _MD_wait(PRThread *thread, PRIntervalTime ticks) +{ + if ( thread->flags & _PR_GLOBAL_SCOPE ) { + _MD_CHECK_FOR_EXIT(); + if (pr_cvar_wait_sem(thread, ticks) == PR_FAILURE) { + _MD_CHECK_FOR_EXIT(); + /* + * wait timed out + */ + _PR_THREAD_LOCK(thread); + if (thread->wait.cvar) { + /* + * The thread will remove itself from the waitQ + * of the cvar in _PR_WaitCondVar + */ + thread->wait.cvar = NULL; + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + } else { + _PR_THREAD_UNLOCK(thread); + /* + * This thread was woken up by a notifying thread + * at the same time as a timeout; so, consume the + * extra post operation on the semaphore + */ + _MD_CHECK_FOR_EXIT(); + pr_cvar_wait_sem(thread, PR_INTERVAL_NO_TIMEOUT); + } + _MD_CHECK_FOR_EXIT(); + } + } else { + _PR_MD_SWITCH_CONTEXT(thread); + } + return PR_SUCCESS; +} + +PRStatus _MD_WakeupWaiter(PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 pid, rv; + PRIntn is; + + PR_ASSERT(_pr_md_idle_cpus >= 0); + if (thread == NULL) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } else if (!_PR_IS_NATIVE_THREAD(thread)) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } else { + PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _MD_CVAR_POST_SEM(thread); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + } + return PR_SUCCESS; +} + +PRStatus _MD_CreateThread(PRThread *thread, +void (*start)(void *), +PRThreadPriority priority, +PRThreadScope scope, +PRThreadState state, +PRUint32 stackSize) +{ + typedef void (*SprocEntry) (void *, size_t); + SprocEntry spentry = (SprocEntry)start; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 pid; + PRStatus rv; + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + thread->md.cvar_pollsem_select = 0; + thread->flags |= _PR_GLOBAL_SCOPE; + pid = sprocsp( + spentry, /* startup func */ + PR_SALL, /* attribute flags */ + (void *)thread, /* thread param */ + NULL, /* stack address */ + stackSize); /* stack size */ + if (pid > 0) { + /* + * Wait for the sproc to signal me after it has initialized + * itself + */ + if (!_PR_IS_NATIVE_THREAD(me)) + blockproc(me->cpu->md.id); + else + blockproc(me->md.id); + if (thread->md.cvar_pollsemfd < 0) { + /* + * the sproc failed to create a polled semaphore and exited + */ + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed); + rv = PR_FAILURE; + } else { + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_created); + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs); + rv = PR_SUCCESS; + } + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return rv; + } else { + _MD_ATOMIC_INCREMENT(&_pr_md_irix_sprocs_failed); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } +} + +void _MD_CleanThread(PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + close(thread->md.cvar_pollsemfd); + thread->md.cvar_pollsemfd = -1; + free_poll_sem(&thread->md); + thread->md.cvar_pollsem = NULL; + } +} + +void _MD_SetPriority(_MDThread *thread, PRThreadPriority newPri) +{ + return; +} + +extern void _MD_unix_terminate_waitpid_daemon(void); + +void +_MD_CleanupBeforeExit(void) +{ + extern PRInt32 _pr_cpus_exit; + + _MD_unix_terminate_waitpid_daemon(); + + _pr_irix_exit_now = 1; + if (_pr_numCPU > 1) { + /* + * Set a global flag, and wakeup all cpus which will notice the flag + * and exit. + */ + _pr_cpus_exit = getpid(); + _MD_Wakeup_CPUs(); + while(_pr_numCPU > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _pr_numCPU--; + } + } + /* + * cause global threads on the recycle list to exit + */ + _PR_DEADQ_LOCK; + if (_PR_NUM_DEADNATIVE != 0) { + PRThread *thread; + PRCList *ptr; + + ptr = _PR_DEADNATIVEQ.next; + while( ptr != &_PR_DEADNATIVEQ ) { + thread = _PR_THREAD_PTR(ptr); + _MD_CVAR_POST_SEM(thread); + ptr = ptr->next; + } + } + _PR_DEADQ_UNLOCK; + while(_PR_NUM_DEADNATIVE > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _PR_DEC_DEADNATIVE; + } +} + +#ifndef IRIX5_3 +extern void __sgi_prda_procmask(int); +#endif + +PRStatus +_MD_InitThread(PRThread *thread, PRBool wakeup_parent) +{ + struct sigaction sigact; + PRStatus rv = PR_SUCCESS; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + /* + * create a polled semaphore + * + * NOTE: On Irix there is a bug which requires the sproc that + * created a polled semaphore to not exit for that semaphore + * to be useable by other sprocs. + */ + thread->md.id = getpid(); + thread->md.cvar_pollsemfd = -1; + if (new_poll_sem(&thread->md,0) == PR_FAILURE) { + if (wakeup_parent == PR_TRUE) + unblockproc(getppid()); + rv = PR_FAILURE; + } + thread->md.cvar_pollsemfd = + _PR_OPEN_POLL_SEM(thread->md.cvar_pollsem); + if ((thread->md.cvar_pollsemfd < 0)) { + free_poll_sem(&thread->md); + if (wakeup_parent == PR_TRUE) + unblockproc(getppid()); + rv = PR_FAILURE; + } + setblockproccnt(thread->md.id, 0); + _MD_SET_SPROC_PID(getpid()); +#ifndef IRIX5_3 + /* + * enable user-level processing of sigprocmask(); this is an + * undocumented feature available in Irix 6.2, 6.3, 6.4 and 6.5 + */ + __sgi_prda_procmask(USER_LEVEL); +#endif + /* + * set up SIGUSR1 handler; this is used to save state + * during PR_SuspendAll + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + /* + * Must mask clock interrupts + */ + sigact.sa_mask = timer_set; + sigaction(SIGUSR1, &sigact, 0); + + + if (_nspr_terminate_on_error) { + /* + * PR_SETABORTSIG is a new command implemented in a patch to + * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all + * sprocs in the process when one of them terminates abnormally + * + */ + + if (prctl(PR_SETABORTSIG, SIGKILL) < 0) { + /* + * if (errno == EINVAL) + * + * PR_SETABORTSIG not supported under this OS. + * You may want to get a recent kernel rollup patch that + * supports this feature. + */ + /* + * SIGCLD handler for detecting abormally-terminating + * sprocs + */ + sigact.sa_handler = sigchld_handler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGCLD, &sigact, NULL); + } + } + /* + * unblock the parent sproc + */ + if (wakeup_parent == PR_TRUE) + unblockproc(getppid()); + } + return rv; +} + +PR_EXTERN(void ) _MD_exit(PRIntn status) +{ + /* + * Cause SIGKILL to be sent to other sprocs, if any, in the application + */ + prctl(PR_SETEXITSIG, SIGKILL); + exit(status); +} + +void +_MD_InitRunningCPU(_PRCPU *cpu) +{ + extern int _pr_md_pipefd[2]; + + _MD_unix_init_running_cpu(cpu); + cpu->md.id = getpid(); + _MD_SET_SPROC_PID(getpid()); + if (_pr_md_pipefd[0] >= 0) { + _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0]; + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu)); + } +} + +void +_MD_ExitThread(PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + _MD_ATOMIC_DECREMENT(&_pr_md_irix_sprocs); + _MD_CLEAN_THREAD(thread); + _MD_SET_CURRENT_THREAD(NULL); + } +} + +void +_MD_SuspendCPU(_PRCPU *cpu) +{ + PRInt32 rv; + + cpu->md.suspending_id = getpid(); + rv = kill(cpu->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); + +} + +void +_MD_ResumeCPU(_PRCPU *cpu) +{ + unblockproc(cpu->md.id); +} + +#if 0 +/* + * save the register context of a suspended sproc + */ +void get_context(PRThread *thr) +{ + int len, fd; + char pidstr[24]; + char path[24]; + + /* + * open the file corresponding to this process in procfs + */ + sprintf(path,"/proc/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",thr->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + (void) ioctl(fd, PIOCGREG, thr->md.gregs); + close(fd); + } + return; +} +#endif /* 0 */ + +void +_MD_SuspendThread(PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + (thread->flags & _PR_GCABLE_THREAD)); + + thread->md.suspending_id = getpid(); + rv = kill(thread->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +} + +void +_MD_ResumeThread(PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + (thread->flags & _PR_GCABLE_THREAD)); + rv = unblockproc(thread->md.id); +} + +/* + * return the set of processors available for scheduling procs in the + * "mask" argument + */ +PRInt32 _MD_GetThreadAffinityMask(PRThread *unused, PRUint32 *mask) +{ + PRInt32 nprocs, rv; + struct pda_stat *pstat; +#define MAX_PROCESSORS 32 + + nprocs = sysmp(MP_NPROCS); + if (nprocs < 0) + return(-1); + pstat = PR_MALLOC(sizeof(struct pda_stat) * nprocs); + if (pstat == NULL) + return(-1); + rv = sysmp(MP_STAT, pstat); + if (rv < 0) { + PR_DELETE(pstat); + return(-1); + } + /* + * look at the first 32 cpus + */ + nprocs = (nprocs > MAX_PROCESSORS) ? MAX_PROCESSORS : nprocs; + *mask = 0; + while (nprocs) { + if ((pstat->p_flags & PDAF_ENABLED) && + !(pstat->p_flags & PDAF_ISOLATED)) { + *mask |= (1 << pstat->p_cpuid); + } + nprocs--; + pstat++; + } + return 0; +} + +static char *_thr_state[] = { + "UNBORN", + "RUNNABLE", + "RUNNING", + "LOCK_WAIT", + "COND_WAIT", + "JOIN_WAIT", + "IO_WAIT", + "SUSPENDED", + "DEAD" +}; + +_PR_List_Threads() +{ + PRThread *thr; + void *handle; + struct _PRCPU *cpu; + PRCList *qp; + int len, status, rv, fd; + char pidstr[24]; + char path[24]; + prpsinfo_t pinfo; + + + printf("\n%s %-s\n"," ","LOCAL Threads"); + printf("%s %-s\n"," ","----- -------"); + printf("%s %-14s %-10s %-12s %-3s %-10s %-10s %-12s\n\n"," ", + "Thread", "State", "Wait-Handle", + "Cpu","Stk-Base","Stk-Sz","SP"); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + thr = _PR_ACTIVE_THREAD_PTR(qp); + printf("%s 0x%-12x %-10s "," ",thr,_thr_state[thr->state]); + if (thr->state == _PR_LOCK_WAIT) + handle = thr->wait.lock; + else if (thr->state == _PR_COND_WAIT) + handle = thr->wait.cvar; + else + handle = NULL; + if (handle) + printf("0x%-10x ",handle); + else + printf("%-12s "," "); + printf("%-3d ",thr->cpu->id); + printf("0x%-8x ",thr->stack->stackBottom); + printf("0x%-8x ",thr->stack->stackSize); + printf("0x%-10x\n",thr->md.jb[JB_SP]); + } + + printf("\n%s %-s\n"," ","GLOBAL Threads"); + printf("%s %-s\n"," ","------ -------"); + printf("%s %-14s %-6s %-12s %-12s %-12s %-12s\n\n"," ","Thread", + "Pid","State","Wait-Handle", + "Stk-Base","Stk-Sz"); + + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + thr = _PR_ACTIVE_THREAD_PTR(qp); + if (thr->cpu != NULL) + continue; /* it is a cpu thread */ + printf("%s 0x%-12x %-6d "," ",thr,thr->md.id); + /* + * check if the sproc is still running + * first call prctl(PR_GETSHMASK,pid) to check if + * the process is part of the share group (the pid + * could have been recycled by the OS) + */ + if (prctl(PR_GETSHMASK,thr->md.id) < 0) { + printf("%-12s\n","TERMINATED"); + continue; + } + /* + * Now, check if the sproc terminated and is in zombie + * state + */ + sprintf(path,"/proc/pinfo/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",thr->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + if (ioctl(fd, PIOCPSINFO, &pinfo) < 0) + printf("%-12s ","TERMINATED"); + else if (pinfo.pr_zomb) + printf("%-12s ","TERMINATED"); + else + printf("%-12s ",_thr_state[thr->state]); + close(fd); + } else { + printf("%-12s ","TERMINATED"); + } + + if (thr->state == _PR_LOCK_WAIT) + handle = thr->wait.lock; + else if (thr->state == _PR_COND_WAIT) + handle = thr->wait.cvar; + else + handle = NULL; + if (handle) + printf("%-12x ",handle); + else + printf("%-12s "," "); + printf("0x%-10x ",thr->stack->stackBottom); + printf("0x%-10x\n",thr->stack->stackSize); + } + + printf("\n%s %-s\n"," ","CPUs"); + printf("%s %-s\n"," ","----"); + printf("%s %-14s %-6s %-12s \n\n"," ","Id","Pid","State"); + + + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { + cpu = _PR_CPU_PTR(qp); + printf("%s %-14d %-6d "," ",cpu->id,cpu->md.id); + /* + * check if the sproc is still running + * first call prctl(PR_GETSHMASK,pid) to check if + * the process is part of the share group (the pid + * could have been recycled by the OS) + */ + if (prctl(PR_GETSHMASK,cpu->md.id) < 0) { + printf("%-12s\n","TERMINATED"); + continue; + } + /* + * Now, check if the sproc terminated and is in zombie + * state + */ + sprintf(path,"/proc/pinfo/%s","00000"); + len = strlen(path); + sprintf(pidstr,"%d",cpu->md.id); + len -= strlen(pidstr); + sprintf(path + len,"%s",pidstr); + fd = open(path,O_RDONLY); + if (fd >= 0) { + if (ioctl(fd, PIOCPSINFO, &pinfo) < 0) + printf("%-12s\n","TERMINATED"); + else if (pinfo.pr_zomb) + printf("%-12s\n","TERMINATED"); + else + printf("%-12s\n","RUNNING"); + close(fd); + } else { + printf("%-12s\n","TERMINATED"); + } + + } + fflush(stdout); +} +#endif /* defined(_PR_PTHREADS) */ + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#if !defined(_PR_PTHREADS) + if (isCurrent) { + (void) setjmp(t->md.jb); + } + *np = sizeof(t->md.jb) / sizeof(PRWord); + return (PRWord *) (t->md.jb); +#else + *np = 0; + return NULL; +#endif +} + +void _MD_EarlyInit(void) +{ +#if !defined(_PR_PTHREADS) + char *eval; + int fd; + + sigemptyset(&ints_off); + sigaddset(&ints_off, SIGALRM); + sigaddset(&ints_off, SIGIO); + sigaddset(&ints_off, SIGCLD); + + if (eval = getenv("_NSPR_TERMINATE_ON_ERROR")) + _nspr_terminate_on_error = (0 == atoi(eval) == 0) ? PR_FALSE : PR_TRUE; + + fd = open("/dev/zero",O_RDWR , 0); + if (fd < 0) { + perror("open /dev/zero failed"); + exit(1); + } + /* + * Set up the sproc private data area. + * This region exists at the same address, _nspr_sproc_private, for + * every sproc, but each sproc gets a private copy of the region. + */ + _nspr_sproc_private = mmap(0, _pr_pageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE| MAP_LOCAL, fd, 0); + if (_nspr_sproc_private == (void*)-1) { + perror("mmap /dev/zero failed"); + exit(1); + } + close(fd); +#endif + _MD_IntervalInit(); +} /* _MD_EarlyInit */ + +void _MD_IrixInit() +{ +#if !defined(_PR_PTHREADS) + struct sigaction sigact; + rlim_t stack_max_limit; + PRThread *me = _PR_MD_CURRENT_THREAD(); + +#ifndef IRIX5_3 + /* + * enable user-level processing of sigprocmask(); this is an undocumented + * feature available in Irix 6.2, 6.3, 6.4 and 6.5 + */ + __sgi_prda_procmask(USER_LEVEL); +#endif + + /* + * set up SIGUSR1 handler; this is used to save state + * during PR_SuspendAll + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGUSR1, &sigact, 0); + + /* + * Change the name of the core file from core to core.pid, + * This is inherited by the sprocs created by this process + */ +#ifndef IRIX5_3 + prctl(PR_COREPID, 0, 1); +#endif + /* + * Irix-specific terminate on error processing + */ + if (_nspr_terminate_on_error) { + /* + * PR_SETABORTSIG is a new command implemented in a patch to + * Irix 6.2, 6.3 and 6.4. This causes a signal to be sent to all + * sprocs in the process when one of them terminates abnormally + * + */ + if (prctl(PR_SETABORTSIG, SIGKILL) < 0) { + /* + * if (errno == EINVAL) + * + * PR_SETABORTSIG not supported under this OS. + * You may want to get a recent kernel rollup patch that + * supports this feature. + * + */ + /* + * PR_SETEXITSIG - send the SIGCLD signal to the parent + * sproc when any sproc terminates + * + * This is used to cause the entire application to + * terminate when any sproc terminates abnormally by + * receipt of a SIGSEGV, SIGBUS or SIGABRT signal. + * If this is not done, the application may seem + * "hung" to the user because the other sprocs may be + * waiting for resources held by the + * abnormally-terminating sproc. + */ + prctl(PR_SETEXITSIG, 0); + + sigact.sa_handler = sigchld_handler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = ints_off; + sigaction(SIGCLD, &sigact, NULL); + } + } + + /* + * setup stack fields for the primordial thread + */ + me->stack->stackSize = prctl(PR_GETSTACKSIZE); + me->stack->stackBottom = me->stack->stackTop - me->stack->stackSize; +#endif /* _PR_PTHREADS */ + + _PR_UnixInit(); +} + +/**************************************************************************/ +/************** code and such for NSPR 2.0's interval times ***************/ +/**************************************************************************/ + +#define PR_CLOCK_GRANULARITY 10000UL + +#ifndef SGI_CYCLECNTR_SIZE +#define SGI_CYCLECNTR_SIZE 165 /* Size user needs to use to read CC */ +#endif + +static PRUintn mmem_fd = -1; +static PRIntn clock_width = 0; +static void *iotimer_addr = NULL; +static PRUint32 pr_clock_mask = 0; +static PRUint32 pr_clock_shift = 0; +static PRIntervalTime pr_ticks = 0; +static PRUint32 pr_clock_granularity = 1; +static PRUint32 pr_previous = 0, pr_residual = 0; + +extern PRIntervalTime _PR_UNIX_GetInterval(void); +extern PRIntervalTime _PR_UNIX_TicksPerSecond(void); + +void _MD_IntervalInit(void) +{ + /* + * As much as I would like, the service available through this + * interface on R3000's (aka, IP12) just isn't going to make it. + * The register is only 24 bits wide, and rolls over at a verocious + * rate. + */ + PRUint32 one_tick = 0; + struct utsname utsinfo; + uname(&utsinfo); + if ((strncmp("IP12", utsinfo.machine, 4) != 0) + && ((mmem_fd = open("/dev/mmem", O_RDONLY)) != -1)) + { + int poffmask = getpagesize() - 1; + __psunsigned_t phys_addr, raddr, cycleval; + + phys_addr = syssgi(SGI_QUERY_CYCLECNTR, &cycleval); + raddr = phys_addr & ~poffmask; + iotimer_addr = mmap( + 0, poffmask, PROT_READ, MAP_PRIVATE, mmem_fd, (__psint_t)raddr); + + clock_width = syssgi(SGI_CYCLECNTR_SIZE); + if (clock_width < 0) + { + /* + * We must be executing on a 6.0 or earlier system, since the + * SGI_CYCLECNTR_SIZE call is not supported. + * + * The only pre-6.1 platforms with 64-bit counters are + * IP19 and IP21 (Challenge, PowerChallenge, Onyx). + */ + if (!strncmp(utsinfo.machine, "IP19", 4) || + !strncmp(utsinfo.machine, "IP21", 4)) + clock_width = 64; + else + clock_width = 32; + } + + /* + * 'cycleval' is picoseconds / increment of the counter. + * I'm pushing for a tick to be 100 microseconds, 10^(-4). + * That leaves 10^(-8) left over, or 10^8 / cycleval. + * Did I do that right? + */ + + one_tick = 100000000UL / cycleval ; /* 100 microseconds */ + + while (0 != one_tick) + { + pr_clock_shift += 1; + one_tick = one_tick >> 1; + pr_clock_granularity = pr_clock_granularity << 1; + } + pr_clock_mask = pr_clock_granularity - 1; /* to make a mask out of it */ + + iotimer_addr = (void*) + ((__psunsigned_t)iotimer_addr + (phys_addr & poffmask)); + } + +} /* _PR_MD_INTERVAL_INIT */ + +PRIntervalTime _MD_IntervalPerSec() +{ + return pr_clock_granularity; +} + +PRIntervalTime _MD_GetInterval() +{ + if (mmem_fd != -1) + { + if (64 == clock_width) + { + PRUint64 temp = *(PRUint64*)iotimer_addr; + pr_ticks = (PRIntervalTime)(temp >> pr_clock_shift); + } + else + { + PRIntervalTime ticks = pr_ticks; + PRUint32 now = *(PRUint32*)iotimer_addr, temp; + PRUint32 residual = pr_residual, previous = pr_previous; + + temp = now - previous + residual; + residual = temp & pr_clock_mask; + ticks += temp >> pr_clock_shift; + + pr_previous = now; + pr_residual = residual; + pr_ticks = ticks; + } + } + else + { + /* + * No fast access. Use the time of day clock. This isn't the + * right answer since this clock can get set back, tick at odd + * rates, and it's expensive to acqurie. + */ + pr_ticks = _PR_UNIX_GetInterval(); + PR_ASSERT(PR_CLOCK_GRANULARITY > _PR_UNIX_TicksPerSecond()); + pr_ticks *= (PR_CLOCK_GRANULARITY / _PR_UNIX_TicksPerSecond()); + } + return pr_ticks; +} /* _MD_GetInterval */ + diff --git a/pr/src/md/unix/linux.c b/pr/src/md/unix/linux.c new file mode 100644 index 00000000..a653f47b --- /dev/null +++ b/pr/src/md/unix/linux.c @@ -0,0 +1,230 @@ +/* -*- 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" + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifdef _PR_PTHREADS + +extern void _MD_unix_terminate_waitpid_daemon(void); + +void _MD_CleanupBeforeExit(void) +{ + _MD_unix_terminate_waitpid_daemon(); +} + +#else /* ! _PR_PTHREADS */ + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for OSF1 */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for OSF1."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ + +#if defined(_PR_NEED_FAKE_POLL) + +#include <fcntl.h> + +int poll(struct pollfd *filedes, unsigned long nfds, int timeout) +{ + int i; + int rv; + int maxfd; + fd_set rd, wr, ex; + struct timeval tv, *tvp; + + if (timeout < 0 && timeout != -1) { + errno = EINVAL; + return -1; + } + + if (timeout == -1) { + tvp = NULL; + } else { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + tvp = &tv; + } + + maxfd = -1; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + for (i = 0; i < nfds; i++) { + int osfd = filedes[i].fd; + int events = filedes[i].events; + PRBool fdHasEvent = PR_FALSE; + + if (osfd < 0) { + continue; /* Skip this osfd. */ + } + + /* + * Map the native poll flags to nspr poll flags. + * POLLIN, POLLRDNORM ===> PR_POLL_READ + * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE + * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPTION + * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) + * are ignored. + * + * The output events POLLERR and POLLHUP are never turned on. + * POLLNVAL may be turned on. + */ + + if (events & (POLLIN | POLLRDNORM)) { + FD_SET(osfd, &rd); + fdHasEvent = PR_TRUE; + } + if (events & (POLLOUT | POLLWRNORM)) { + FD_SET(osfd, &wr); + fdHasEvent = PR_TRUE; + } + if (events & (POLLPRI | POLLRDBAND)) { + FD_SET(osfd, &ex); + fdHasEvent = PR_TRUE; + } + if (fdHasEvent && osfd > maxfd) { + maxfd = osfd; + } + } + + rv = select(maxfd + 1, &rd, &wr, &ex, tvp); + + /* Compute poll results */ + if (rv > 0) { + rv = 0; + for (i = 0; i < nfds; i++) { + PRBool fdHasEvent = PR_FALSE; + + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (FD_ISSET(filedes[i].fd, &rd)) { + if (filedes[i].events & POLLIN) { + filedes[i].revents |= POLLIN; + } + if (filedes[i].events & POLLRDNORM) { + filedes[i].revents |= POLLRDNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &wr)) { + if (filedes[i].events & POLLOUT) { + filedes[i].revents |= POLLOUT; + } + if (filedes[i].events & POLLWRNORM) { + filedes[i].revents |= POLLWRNORM; + } + fdHasEvent = PR_TRUE; + } + if (FD_ISSET(filedes[i].fd, &ex)) { + if (filedes[i].events & POLLPRI) { + filedes[i].revents |= POLLPRI; + } + if (filedes[i].events & POLLRDBAND) { + filedes[i].revents |= POLLRDBAND; + } + fdHasEvent = PR_TRUE; + } + if (fdHasEvent) { + rv++; + } + } + PR_ASSERT(rv > 0); + } else if (rv == -1 && errno == EBADF) { + rv = 0; + for (i = 0; i < nfds; i++) { + filedes[i].revents = 0; + if (filedes[i].fd < 0) { + continue; + } + if (fcntl(filedes[i].fd, F_GETFL, 0) == -1) { + filedes[i].revents = POLLNVAL; + rv++; + } + } + PR_ASSERT(rv > 0); + } + PR_ASSERT(-1 != timeout || rv != 0); + + return rv; +} +#endif /* _PR_NEED_FAKE_POLL */ diff --git a/pr/src/md/unix/ncr.c b/pr/src/md/unix/ncr.c new file mode 100644 index 00000000..1560cc2c --- /dev/null +++ b/pr/src/md/unix/ncr.c @@ -0,0 +1,367 @@ +/* -*- 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. + */ + +/* + * NCR 3.0 - cloned from UnixWare by ruslan + */ +#include "primpl.h" + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +/* + * These are also implemented in pratom.c using NSPR locks. Any reason + * this might be better or worse? If you like this better, define + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h + */ +#ifdef _PR_HAVE_ATOMIC_OPS +/* Atomic operations */ +#include <stdio.h> +static FILE *_uw_semf; + +void +_MD_INIT_ATOMIC(void) +{ + /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ + if ((_uw_semf = tmpfile()) == NULL) + PR_ASSERT(0); + + return; +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)++; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)--; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + flockfile(_uw_semf); + *val = newval; + unflockfile(_uw_semf); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Unixware */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Unixware."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRUintn priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware."); + return PR_FAILURE; +} + +/* + This is temp. replacement for localtime_r. Normally PR_ExplodeTime should + be used as to my understanding +*/ + +/* +** $$$$$ THEN WHY ARE WE DOING THIS? - AOF $$$$$ +*/ + +#define NEED_LOCALTIME_R +#define NEED_GMTIME_R +#define NEED_ASCTIME_R +#define NEED_STRTOK_R +#define NEED_CTIME_R + +#if defined (NEED_LOCALTIME_R) || defined (NEED_CTIME_R) || defined (NEED_ASCTIME_R) || defined (NEED_GMTIME_R) || defined (NEED_STRTOK_R) +#include "prlock.h" +#endif + +#if defined (NEED_LOCALTIME_R) + +static PRLock *localtime_r_monitor = NULL; + +struct tm *localtime_r (const time_t *clock, struct tm *result) +{ + struct tm *tmPtr; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + if (localtime_r_monitor == NULL) { + + localtime_r_monitor = PR_NewLock(); + } + PR_Lock(localtime_r_monitor); + } + + /* + * On Windows, localtime() returns a NULL pointer if 'clock' + * represents a time before midnight January 1, 1970. In + * that case, we also return a NULL pointer and the struct tm + * object pointed to by 'result' is not modified. + */ + + tmPtr = localtime(clock); + if (tmPtr) { + *result = *tmPtr; + } else { + result = NULL; + } + + if (needLock) PR_Unlock(localtime_r_monitor); + + return result; +} + +#endif + +#if defined (NEED_GMTIME_R) + +static PRLock *gmtime_r_monitor = NULL; + +struct tm *gmtime_r (const time_t *clock, struct tm *result) +{ + struct tm *tmPtr; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + if (gmtime_r_monitor == NULL) { + gmtime_r_monitor = PR_NewLock(); + } + PR_Lock(gmtime_r_monitor); + } + + tmPtr = gmtime(clock); + if (tmPtr) { + *result = *tmPtr; + } else { + result = NULL; + } + + if (needLock) PR_Unlock(gmtime_r_monitor); + + return result; +} + +#endif + +#if defined (NEED_CTIME_R) + +static PRLock *ctime_r_monitor = NULL; + +char *ctime_r (const time_t *clock, char *buf, int buflen) +{ + char *cbuf; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + + if (ctime_r_monitor == NULL) { + ctime_r_monitor = PR_NewLock(); + } + PR_Lock(ctime_r_monitor); + } + + cbuf = ctime (clock); + if (cbuf) { + strncpy (buf, cbuf, buflen - 1); + buf[buflen - 1] = 0; + } + + if (needLock) PR_Unlock(ctime_r_monitor); + + return cbuf; +} + +#endif + +#if defined (NEED_ASCTIME_R) + +static PRLock *asctime_r_monitor = NULL; + + +char *asctime_r (const struct tm *tm, char *buf, int buflen) +{ + char *cbuf; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + if (asctime_r_monitor == NULL) { + asctime_r_monitor = PR_NewLock(); + } + PR_Lock(asctime_r_monitor); + } + + cbuf = asctime (tm); + if (cbuf) { + strncpy (buf, cbuf, buflen - 1); + buf[buflen - 1] = 0; + } + + if (needLock) PR_Unlock(asctime_r_monitor); + + return cbuf; + +} +#endif + +#if defined (NEED_STRTOK_R) + +char * +strtok_r (s, delim, last) + register char *s; + register const char *delim; + register char **last; +{ + register char *spanp; + register int c, sc; + char *tok; + + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + + else + s[-1] = 0; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +#endif diff --git a/pr/src/md/unix/nec.c b/pr/src/md/unix/nec.c new file mode 100644 index 00000000..3b466cfa --- /dev/null +++ b/pr/src/md/unix/nec.c @@ -0,0 +1,81 @@ +/* -*- 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" + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for NEC */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for NEC."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for NEC."); + return PR_FAILURE; +} diff --git a/pr/src/md/unix/objs.mk b/pr/src/md/unix/objs.mk new file mode 100644 index 00000000..7882ce27 --- /dev/null +++ b/pr/src/md/unix/objs.mk @@ -0,0 +1,176 @@ +# +# 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 makefile appends to the variable OBJS the platform-dependent +# object modules that will be part of the nspr20 library. + +CSRCS = \ + unix.c \ + unix_errors.c \ + uxproces.c \ + uxwrap.c \ + $(NULL) + +PTH_USER_CSRCS = \ + pthreads_user.c \ + $(NULL) + +IRIX_CSRCS = \ + irix.c \ + $(NULL) + +SUNOS4_CSRCS = \ + sunos4.c \ + $(NULL) + +SOLARIS_CSRCS = \ + solaris.c \ + $(NULL) + +AIX_CSRCS = \ + aix.c \ + $(NULL) + +FREEBSD_CSRCS = \ + freebsd.c \ + $(NULL) + +BSDI_CSRCS = \ + bsdi.c \ + $(NULL) + +HPUX_CSRCS = \ + hpux.c \ + $(NULL) + +OSF1_CSRCS = \ + osf1.c \ + $(NULL) + +LINUX_CSRCS = \ + linux.c \ + $(NULL) + +UNIXWARE_CSRCS = \ + unixware.c \ + $(NULL) + +RELIANTUNIX_CSRCS = \ + reliantunix.c \ + $(NULL) + +NEC_CSRCS = \ + nec.c \ + $(NULL) + +SONY_CSRCS = \ + sony.c \ + $(NULL) + +NCR_CSRCS = \ + ncr.c \ + $(NULL) + +SCOOS_CSRCS = \ + scoos.c \ + $(NULL) + + +ifeq ($(PTHREADS_USER),1) +CSRCS += $(PTH_USER_CSRCS) +endif + +ifeq ($(OS_ARCH),IRIX) +CSRCS += $(IRIX_CSRCS) +endif + +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +CSRCS += $(SUNOS4_CSRCS) +else +CSRCS += $(SOLARIS_CSRCS) +endif +endif + +ifeq ($(OS_ARCH),AIX) +CSRCS += $(AIX_CSRCS) +endif +ifeq ($(OS_ARCH),FreeBSD) +CSRCS += $(FREEBSD_CSRCS) +endif +ifeq ($(OS_ARCH),BSD_386) +CSRCS += $(BSDI_CSRCS) +endif +ifeq ($(OS_ARCH),HP-UX) +CSRCS += $(HPUX_CSRCS) +endif +ifeq ($(OS_ARCH),OSF1) +CSRCS += $(OSF1_CSRCS) +endif +ifeq ($(OS_ARCH),Linux) +CSRCS += $(LINUX_CSRCS) +endif +ifeq ($(OS_ARCH),UNIXWARE) +CSRCS += $(UNIXWARE_CSRCS) +endif +ifeq ($(OS_ARCH),ReliantUNIX) +CSRCS += $(RELIANTUNIX_CSRCS) +endif +ifeq ($(OS_ARCH),NEC) +CSRCS += $(NEC_CSRCS) +endif +ifeq ($(OS_ARCH),NEWS-OS) +CSRCS += $(SONY_CSRCS) +endif +ifeq ($(OS_ARCH),NCR) +CSRCS += $(NCR_CSRCS) +endif +ifeq ($(OS_ARCH),SCO_SV) +CSRCS += $(SCOOS_CSRCS) +endif + +# +# Some Unix platforms have an assembly language file. +# E.g., AIX 3.2, Solaris (both sparc and x86). +# +ifeq ($(OS_ARCH), AIX) + ifeq ($(OS_RELEASE), 3.2) + ASFILES = os_$(OS_ARCH).s + endif +endif + +ifeq ($(OS_ARCH),SunOS) + ifeq ($(OS_TEST),i86pc) + ASFILES = os_$(OS_ARCH)_x86.s + else + ifneq ($(OS_RELEASE),4.1.3_U1) + ASFILES = os_$(OS_ARCH).s + endif + endif +endif + +ifeq ($(OS_ARCH), ReliantUNIX) + ASFILES = os_$(OS_ARCH).s +endif + +ifeq ($(OS_ARCH)$(OS_RELEASE),BSD_3862.1) + ASFILES = os_BSD_386_2.s +endif + +OBJS += $(addprefix md/unix/$(OBJDIR)/,$(CSRCS:.c=.o)) \ + $(addprefix md/unix/$(OBJDIR)/,$(ASFILES:.s=.o)) + diff --git a/pr/src/md/unix/os_AIX.s b/pr/src/md/unix/os_AIX.s new file mode 100644 index 00000000..63ca4e78 --- /dev/null +++ b/pr/src/md/unix/os_AIX.s @@ -0,0 +1,102 @@ +# -*- 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. +# +.set r0,0; .set SP,1; .set RTOC,2; .set r3,3; .set r4,4 +.set r5,5; .set r6,6; .set r7,7; .set r8,8; .set r9,9 +.set r10,10; .set r11,11; .set r12,12; .set r13,13; .set r14,14 +.set r15,15; .set r16,16; .set r17,17; .set r18,18; .set r19,19 +.set r20,20; .set r21,21; .set r22,22; .set r23,23; .set r24,24 +.set r25,25; .set r26,26; .set r27,27; .set r28,28; .set r29,29 +.set r30,30; .set r31,31 + + + .rename H.10.NO_SYMBOL{PR},"" + .rename H.18.longjmp{TC},"longjmp" + + .lglobl H.10.NO_SYMBOL{PR} + .globl .longjmp + .globl longjmp{DS} + .extern .sigcleanup + .extern .jmprestfpr + +# .text section + + .csect H.10.NO_SYMBOL{PR} +.longjmp: + mr r13,r3 + mr r14,r4 + stu SP,-56(SP) + bl .sigcleanup + l RTOC,0x14(SP) + cal SP,0x38(SP) + mr r3,r13 + mr r4,r14 + l r5,0x8(r3) + l SP,0xc(r3) + l r7,0xf8(r3) + st r7,0x0(SP) + l RTOC,0x10(r3) + bl .jmprestfpr +# 1 == cr0 in disassembly + cmpi 1,r4,0x0 + mtlr r5 + lm r13,0x14(r3) + l r5,0x60(r3) + mtcrf 0x38,r5 + mr r3,r4 + bne __L1 + lil r3,0x1 +__L1: + br + +# traceback table + .long 0x00000000 + .byte 0x00 # VERSION=0 + .byte 0x00 # LANG=TB_C + .byte 0x20 # IS_GL=0,IS_EPROL=0,HAS_TBOFF=1 + # INT_PROC=0,HAS_CTL=0,TOCLESS=0 + # FP_PRESENT=0,LOG_ABORT=0 + .byte 0x40 # INT_HNDL=0,NAME_PRESENT=1 + # USES_ALLOCA=0,CL_DIS_INV=WALK_ONCOND + # SAVES_CR=0,SAVES_LR=0 + .byte 0x80 # STORES_BC=1,FPR_SAVED=0 + .byte 0x00 # GPR_SAVED=0 + .byte 0x02 # FIXEDPARMS=2 + .byte 0x01 # FLOATPARMS=0,PARMSONSTK=1 + .long 0x00000000 # + .long 0x00000014 # TB_OFFSET + .short 7 # NAME_LEN + .byte "longjmp" + .byte 0 # padding + .byte 0 # padding + .byte 0 # padding +# End of traceback table + .long 0x00000000 # "\0\0\0\0" + +# .data section + + .toc # 0x00000038 +T.18.longjmp: + .tc H.18.longjmp{TC},longjmp{DS} + + .csect longjmp{DS} + .long .longjmp # "\0\0\0\0" + .long TOC{TC0} # "\0\0\0008" + .long 0x00000000 # "\0\0\0\0" +# End csect longjmp{DS} + +# .bss section diff --git a/pr/src/md/unix/os_BSD_386_2.s b/pr/src/md/unix/os_BSD_386_2.s new file mode 100644 index 00000000..971ac1b2 --- /dev/null +++ b/pr/src/md/unix/os_BSD_386_2.s @@ -0,0 +1,54 @@ +/* -*- 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. + */ +/* + * os_BSD_386_2.s + * We need to define our own setjmp/longjmp on BSDI 2.x because libc's + * implementation does some sanity checking that defeats user level threads. + * This should no longer be necessary in BSDI 3.0. + */ + +.globl _setjmp +.align 2 +_setjmp: + movl 4(%esp),%eax + movl 0(%esp),%edx + movl %edx, 0(%eax) /* rta */ + movl %ebx, 4(%eax) + movl %esp, 8(%eax) + movl %ebp,12(%eax) + movl %esi,16(%eax) + movl %edi,20(%eax) + movl $0,%eax + ret + +.globl _longjmp +.align 2 +_longjmp: + movl 4(%esp),%edx + movl 8(%esp),%eax + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret diff --git a/pr/src/md/unix/os_ReliantUNIX.s b/pr/src/md/unix/os_ReliantUNIX.s new file mode 100644 index 00000000..4ac8b0c1 --- /dev/null +++ b/pr/src/md/unix/os_ReliantUNIX.s @@ -0,0 +1,108 @@ +/* -*- 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. + */ +/* We want position independent code */ +#define PIC + +#include <sys/asm.h> +#include <sys/regdef.h> +#include <sys/syscall.h> + + .file 1 "os_ReliantUNIX.s" + .option pic2 + .text + + .align 2 + .globl getcxt + .ent getcxt +getcxt: + .frame sp,0,$31 # vars= 0, regs= 0/0, args= 0, extra= 0 + # saved integer regs + sw ra,180(a0) # gpregs[CXT_EPC] + sw gp,152(a0) # gpregs[CXT_GP] + sw sp,156(a0) # gpregs[CXT_SP] + sw s8,160(a0) # gpregs[CXT_S8] + sw s0,104(a0) # gpregs[CXT_S0] + sw s1,108(a0) # gpregs[CXT_S1] + sw s2,112(a0) # gpregs[CXT_S2] + sw s3,116(a0) # gpregs[CXT_S3] + sw s4,120(a0) # gpregs[CXT_S4] + sw s5,124(a0) # gpregs[CXT_S5] + sw s6,128(a0) # gpregs[CXT_S6] + sw s7,132(a0) # gpregs[CXT_S7] + # csr + cfc1 v0,$31 + # saved float regs + s.d $f20,264(a0) # fpregs.fp_r.fp_dregs[10] + s.d $f22,272(a0) # fpregs.fp_r.fp_dregs[11] + s.d $f24,280(a0) # fpregs.fp_r.fp_dregs[12] + s.d $f26,288(a0) # fpregs.fp_r.fp_dregs[13] + s.d $f28,296(a0) # fpregs.fp_r.fp_dregs[14] + s.d $f30,304(a0) # fpregs.fp_r.fp_dregs[15] + sw v0,312(a0) # fpregs.fp_csr + + # give no illusions about the contents + li v0,0x0c # UC_CPU | UC_MAU + sw v0,0(a0) # uc_flags + + move v0,zero + j ra + .end getcxt + + .align 2 + .globl setcxt + .ent setcxt +setcxt: + .frame sp,0,$31 # vars= 0, regs= 0/0, args= 0, extra= 0 + lw v0,312(a0) # fpregs.fp_csr + li v1,0xfffc0fff # mask out exception cause bits + and v0,v0,v1 + # saved integer regs + lw t9,180(a0) # gpregs[CXT_EPC] + lw ra,180(a0) # gpregs[CXT_EPC] + lw gp,152(a0) # gpregs[CXT_GP] + lw sp,156(a0) # gpregs[CXT_SP] + ctc1 v0,$31 # fp_csr + lw s8,160(a0) # gpregs[CXT_S8] + lw s0,104(a0) # gpregs[CXT_S0] + lw s1,108(a0) # gpregs[CXT_S1] + lw s2,112(a0) # gpregs[CXT_S2] + lw s3,116(a0) # gpregs[CXT_S3] + lw s4,120(a0) # gpregs[CXT_S4] + lw s5,124(a0) # gpregs[CXT_S5] + lw s6,128(a0) # gpregs[CXT_S6] + lw s7,132(a0) # gpregs[CXT_S7] + # saved float regs + l.d $f20,264(a0) # fpregs.fp_r.fp_dregs[10] + l.d $f22,272(a0) # fpregs.fp_r.fp_dregs[11] + l.d $f24,280(a0) # fpregs.fp_r.fp_dregs[12] + l.d $f26,288(a0) # fpregs.fp_r.fp_dregs[13] + l.d $f28,296(a0) # fpregs.fp_r.fp_dregs[14] + l.d $f30,304(a0) # fpregs.fp_r.fp_dregs[15] + + # load these, too + # they were not saved, but maybe the user modified them... + lw v0,48(a0) + lw v1,52(a0) + lw a1,60(a0) + lw a2,64(a0) + lw a3,68(a0) + lw a0,56(a0) # there is no way back + + j ra + + .end setcxt diff --git a/pr/src/md/unix/os_SunOS.s b/pr/src/md/unix/os_SunOS.s new file mode 100644 index 00000000..2edf276f --- /dev/null +++ b/pr/src/md/unix/os_SunOS.s @@ -0,0 +1,51 @@ +/* -*- 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. + */ + .text + +/* + * sol_getsp() + * + * Return the current sp (for debugging) + */ + .global sol_getsp +sol_getsp: + retl + mov %sp, %o0 + + +/* + * sol_curthread() + * + * Return a unique identifier for the currently active thread. + */ + .global sol_curthread +sol_curthread: + retl + mov %g7, %o0 + + + .global __MD_FlushRegisterWindows + .global _MD_FlushRegisterWindows + +__MD_FlushRegisterWindows: +_MD_FlushRegisterWindows: + + ta 3 + ret + restore + diff --git a/pr/src/md/unix/os_SunOS_ultrasparc.s b/pr/src/md/unix/os_SunOS_ultrasparc.s new file mode 100644 index 00000000..b98b820f --- /dev/null +++ b/pr/src/md/unix/os_SunOS_ultrasparc.s @@ -0,0 +1,157 @@ +! -*- 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. +! +! +! atomic increment, decrement and swap routines for V8+ sparc (ultrasparc) +! using CAS (compare-and-swap) atomic instructions +! +! this MUST be compiled with an ultrasparc-aware assembler +! +! standard asm linkage macros; this module must be compiled +! with the -P option (use C preprocessor) + +#include <sys/asm_linkage.h> + +! ====================================================================== +! +! Perform the sequence a = a + 1 atomically with respect to other +! fetch-and-adds to location a in a wait-free fashion. +! +! usage : val = PR_AtomicIncrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicIncrement) ! standard assembler/ELF prologue + +retryAI: + ld [%o0], %o2 ! set o2 to the current value + add %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAI ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(PR_AtomicIncrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = a - 1 atomically with respect to other +! fetch-and-decs to location a in a wait-free fashion. +! +! usage : val = PR_AtomicDecrement(address) +! return: current value (you'd think this would be old val) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [local] - work register +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicDecrement) ! standard assembler/ELF prologue + +retryAD: + ld [%o0], %o2 ! set o2 to the current value + sub %o2, 0x1, %o3 ! calc the new value + mov %o3, %o1 ! save the return value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAD ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o1, %o0 ! set the return code to the new value + + SET_SIZE(PR_AtomicDecrement) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! + +! ====================================================================== +! +! Perform the sequence a = b atomically with respect to other +! fetch-and-stores to location a in a wait-free fashion. +! +! usage : old_val = PR_AtomicSet(address, newval) +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created; +! we use the caller's stack frame so what would normally be %i (input) +! registers are actually %o (output registers). Also, we must not +! overwrite the contents of %l (local) registers as they are not +! assumed to be volatile during calls. +! +! So, the registers used are: +! %o0 [input] - the address of the value to increment +! %o1 [input] - the new value to set for [%o0] +! %o2 [local] - work register +! %o3 [local] - work register +! ----------------------- + + ENTRY(PR_AtomicSet) ! standard assembler/ELF prologue + +retryAS: + ld [%o0], %o2 ! set o2 to the current value + mov %o1, %o3 ! set up the new value + cas [%o0], %o2, %o3 ! atomically set if o0 hasn't changed + cmp %o2, %o3 ! see if we set the value + bne retryAS ! if not, try again + nop ! empty out the branch pipeline + retl ! return back to the caller + mov %o3, %o0 ! set the return code to the prev value + + SET_SIZE(PR_AtomicSet) ! standard assembler/ELF epilogue + +! +! end +! +! ====================================================================== +! diff --git a/pr/src/md/unix/os_SunOS_x86.s b/pr/src/md/unix/os_SunOS_x86.s new file mode 100644 index 00000000..867fd2d3 --- /dev/null +++ b/pr/src/md/unix/os_SunOS_x86.s @@ -0,0 +1,60 @@ +/ -*- 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. +/ + .text + + .globl getedi +getedi: + movl %edi,%eax + ret + .type getedi,@function + .size getedi,.-getedi + + .globl setedi +setedi: + movl 4(%esp),%edi + ret + .type setedi,@function + .size setedi,.-setedi + + .globl __MD_FlushRegisterWindows + .globl _MD_FlushRegisterWindows + +__MD_FlushRegisterWindows: +_MD_FlushRegisterWindows: + + ret + +/ +/ sol_getsp() +/ +/ Return the current sp (for debugging) +/ + .globl sol_getsp +sol_getsp: + movl %esp, %eax + ret + +/ +/ sol_curthread() +/ +/ Return a unique identifier for the currently active thread. +/ + .globl sol_curthread +sol_curthread: + movl %ecx, %eax + ret diff --git a/pr/src/md/unix/osf1.c b/pr/src/md/unix/osf1.c new file mode 100644 index 00000000..e79e6607 --- /dev/null +++ b/pr/src/md/unix/osf1.c @@ -0,0 +1,88 @@ +/* -*- 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" + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for OSF1 */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for OSF1."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for OSF1."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/pr/src/md/unix/pthreads_user.c b/pr/src/md/unix/pthreads_user.c new file mode 100644 index 00000000..efc3ff70 --- /dev/null +++ b/pr/src/md/unix/pthreads_user.c @@ -0,0 +1,465 @@ +/* -*- 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 <unistd.h> +#include <signal.h> +#include <pthread.h> + + +sigset_t ints_off; +pthread_mutex_t _pr_heapLock; +pthread_key_t current_thread_key; +pthread_key_t current_cpu_key; +pthread_key_t last_thread_key; +pthread_key_t intsoff_key; + + +PRInt32 _pr_md_pthreads_created, _pr_md_pthreads_failed; +PRInt32 _pr_md_pthreads = 1; + +void _MD_EarlyInit(void) +{ +extern PRInt32 _nspr_noclock; + +#ifdef HPUX + _MD_hpux_install_sigfpe_handler(); +#endif + + if (pthread_key_create(¤t_thread_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(¤t_cpu_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(&last_thread_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + if (pthread_key_create(&intsoff_key, NULL) != 0) { + perror("pthread_key_create failed"); + exit(1); + } + + sigemptyset(&ints_off); + sigaddset(&ints_off, SIGALRM); + sigaddset(&ints_off, SIGIO); + sigaddset(&ints_off, SIGCLD); + + /* + * disable clock interrupts + */ + _nspr_noclock = 1; + +} + +void _MD_InitLocks() +{ + if (pthread_mutex_init(&_pr_heapLock, NULL) != 0) { + perror("pthread_mutex_init failed"); + exit(1); + } +} + +PR_IMPLEMENT(void) _MD_FREE_LOCK(struct _MDLock *lockp) +{ + PRIntn _is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(_is); + pthread_mutex_destroy(&lockp->mutex); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(_is); +} + + + +PR_IMPLEMENT(PRStatus) _MD_NEW_LOCK(struct _MDLock *lockp) +{ + PRStatus rv; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + rv = pthread_mutex_init(&lockp->mutex, NULL); + if (me && !_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; +} + + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +PR_IMPLEMENT(void) +_MD_SetPriority(_MDThread *thread, PRThreadPriority newPri) +{ + /* + * XXX - to be implemented + */ + return; +} + +PR_IMPLEMENT(PRStatus) _MD_InitThread(struct PRThread *thread) +{ + struct sigaction sigact; + + if (thread->flags & _PR_GLOBAL_SCOPE) { + thread->md.pthread = pthread_self(); +#if 0 + /* + * set up SIGUSR1 handler; this is used to save state + * during PR_SuspendAll + */ + sigact.sa_handler = save_context_and_block; + sigact.sa_flags = SA_RESTART; + /* + * Must mask clock interrupts + */ + sigact.sa_mask = timer_set; + sigaction(SIGUSR1, &sigact, 0); +#endif + } + + return PR_SUCCESS; +} + +PR_IMPLEMENT(void) _MD_ExitThread(struct PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + _MD_CLEAN_THREAD(thread); + _MD_SET_CURRENT_THREAD(NULL); + } +} + +PR_IMPLEMENT(void) _MD_CleanThread(struct PRThread *thread) +{ + if (thread->flags & _PR_GLOBAL_SCOPE) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + } +} + +PR_IMPLEMENT(void) _MD_SuspendThread(struct PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + (thread->flags & _PR_GCABLE_THREAD)); +#if 0 + thread->md.suspending_id = getpid(); + rv = kill(thread->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +#endif +} + +PR_IMPLEMENT(void) _MD_ResumeThread(struct PRThread *thread) +{ + PRInt32 rv; + + PR_ASSERT((thread->flags & _PR_GLOBAL_SCOPE) && + (thread->flags & _PR_GCABLE_THREAD)); +#if 0 + rv = unblockproc(thread->md.id); +#endif +} + +PR_IMPLEMENT(void) _MD_SuspendCPU(struct _PRCPU *thread) +{ + PRInt32 rv; + +#if 0 + cpu->md.suspending_id = getpid(); + rv = kill(cpu->md.id, SIGUSR1); + PR_ASSERT(rv == 0); + /* + * now, block the current thread/cpu until woken up by the suspended + * thread from it's SIGUSR1 signal handler + */ + blockproc(getpid()); +#endif +} + +PR_IMPLEMENT(void) _MD_ResumeCPU(struct _PRCPU *thread) +{ +#if 0 + unblockproc(cpu->md.id); +#endif +} + + +#define PT_NANOPERMICRO 1000UL +#define PT_BILLION 1000000000UL + +PR_IMPLEMENT(PRStatus) +_pt_wait(PRThread *thread, PRIntervalTime timeout) +{ +int rv; +struct timeval now; +struct timespec tmo; +PRUint32 ticks = PR_TicksPerSecond(); + + + if (timeout != PR_INTERVAL_NO_TIMEOUT) { + tmo.tv_sec = timeout / ticks; + tmo.tv_nsec = timeout - (tmo.tv_sec * ticks); + tmo.tv_nsec = PR_IntervalToMicroseconds(PT_NANOPERMICRO * + tmo.tv_nsec); + + /* pthreads wants this in absolute time, off we go ... */ +#if defined(SOLARIS) && defined(_SVID_GETTOD) + (void)gettimeofday(&now); +#else + (void)gettimeofday(&now, NULL); +#endif + /* that one's usecs, this one's nsecs - grrrr! */ + tmo.tv_sec += now.tv_sec; + tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); + tmo.tv_sec += tmo.tv_nsec / PT_BILLION; + tmo.tv_nsec %= PT_BILLION; + } + + pthread_mutex_lock(&thread->md.pthread_mutex); + thread->md.wait--; + if (thread->md.wait < 0) { + if (timeout != PR_INTERVAL_NO_TIMEOUT) { + rv = pthread_cond_timedwait(&thread->md.pthread_cond, + &thread->md.pthread_mutex, &tmo); + } + else + rv = pthread_cond_wait(&thread->md.pthread_cond, + &thread->md.pthread_mutex); + if (rv != 0) { + thread->md.wait++; + } + } else + rv = 0; + pthread_mutex_unlock(&thread->md.pthread_mutex); + + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_wait(PRThread *thread, PRIntervalTime ticks) +{ + if ( thread->flags & _PR_GLOBAL_SCOPE ) { + _MD_CHECK_FOR_EXIT(); + if (_pt_wait(thread, ticks) == PR_FAILURE) { + _MD_CHECK_FOR_EXIT(); + /* + * wait timed out + */ + _PR_THREAD_LOCK(thread); + if (thread->wait.cvar) { + /* + * The thread will remove itself from the waitQ + * of the cvar in _PR_WaitCondVar + */ + thread->wait.cvar = NULL; + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + } else { + _pt_wait(thread, PR_INTERVAL_NO_TIMEOUT); + _PR_THREAD_UNLOCK(thread); + } + } + } else { + _PR_MD_SWITCH_CONTEXT(thread); + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) +_MD_WakeupWaiter(PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 pid, rv; + PRIntn is; + + PR_ASSERT(_pr_md_idle_cpus >= 0); + if (thread == NULL) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } else if (!_PR_IS_NATIVE_THREAD(thread)) { + /* + * If the thread is on my cpu's runq there is no need to + * wakeup any cpus + */ + if (!_PR_IS_NATIVE_THREAD(me)) { + if (me->cpu != thread->cpu) { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } + } else { + if (_pr_md_idle_cpus) + _MD_Wakeup_CPUs(); + } + } else { + PR_ASSERT(_PR_IS_NATIVE_THREAD(thread)); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + + pthread_mutex_lock(&thread->md.pthread_mutex); + thread->md.wait++; + rv = pthread_cond_signal(&thread->md.pthread_cond); + PR_ASSERT(rv == 0); + pthread_mutex_unlock(&thread->md.pthread_mutex); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + } + return PR_SUCCESS; +} + +/* These functions should not be called for AIX */ +PR_IMPLEMENT(void) +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for AIX."); +} + +PR_IMPLEMENT(PRStatus) +_MD_CreateThread( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + pthread_attr_t attr; + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + + if (pthread_mutex_init(&thread->md.pthread_mutex, NULL) != 0) { + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } + + if (pthread_cond_init(&thread->md.pthread_cond, NULL) != 0) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } + thread->flags |= _PR_GLOBAL_SCOPE; + + pthread_attr_init(&attr); /* initialize attr with default attributes */ + if (pthread_attr_setstacksize(&attr, (size_t) stackSize) != 0) { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + pthread_attr_destroy(&attr); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } + + thread->md.wait = 0; + if (pthread_create(&thread->md.pthread, &attr, start, (void *)thread) + == 0) { + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_created); + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_SUCCESS; + } else { + pthread_mutex_destroy(&thread->md.pthread_mutex); + pthread_cond_destroy(&thread->md.pthread_cond); + pthread_attr_destroy(&attr); + _MD_ATOMIC_INCREMENT(&_pr_md_pthreads_failed); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return PR_FAILURE; + } +} + +PR_IMPLEMENT(void) +_MD_InitRunningCPU(struct _PRCPU *cpu) +{ + extern int _pr_md_pipefd[2]; + + _MD_unix_init_running_cpu(cpu); + cpu->md.pthread = pthread_self(); + if (_pr_md_pipefd[0] >= 0) { + _PR_IOQ_MAX_OSFD(cpu) = _pr_md_pipefd[0]; + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(cpu)); + } +} + + +void +_MD_CleanupBeforeExit(void) +{ +#if 0 + extern PRInt32 _pr_cpus_exit; + + _pr_irix_exit_now = 1; + if (_pr_numCPU > 1) { + /* + * Set a global flag, and wakeup all cpus which will notice the flag + * and exit. + */ + _pr_cpus_exit = getpid(); + _MD_Wakeup_CPUs(); + while(_pr_numCPU > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _pr_numCPU--; + } + } + /* + * cause global threads on the recycle list to exit + */ + _PR_DEADQ_LOCK; + if (_PR_NUM_DEADNATIVE != 0) { + PRThread *thread; + PRCList *ptr; + + ptr = _PR_DEADNATIVEQ.next; + while( ptr != &_PR_DEADNATIVEQ ) { + thread = _PR_THREAD_PTR(ptr); + _MD_CVAR_POST_SEM(thread); + ptr = ptr->next; + } + } + _PR_DEADQ_UNLOCK; + while(_PR_NUM_DEADNATIVE > 1) { + _PR_WAIT_SEM(_pr_irix_exit_sem); + _PR_DEC_DEADNATIVE; + } +#endif +} diff --git a/pr/src/md/unix/reliantunix.c b/pr/src/md/unix/reliantunix.c new file mode 100644 index 00000000..755d7f8c --- /dev/null +++ b/pr/src/md/unix/reliantunix.c @@ -0,0 +1,114 @@ +/* -*- 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. + */ + +/* + * ReliantUNIX5.4 - copied from unixware.c by chrisk 040497 + */ +#include "primpl.h" + +#include <ucontext.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) _GETCONTEXT(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for ReliantUNIX */ +/* Why? Just copied it from UNIXWARE... flying-by-night, chrisk 040497 */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for ReliantUNIX."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRUintn priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for ReliantUNIX."); +#if defined(SNI) && !defined(__GNUC__) + /* make compiler happy */ + return (PRStatus)NULL; +#endif +} diff --git a/pr/src/md/unix/scoos.c b/pr/src/md/unix/scoos.c new file mode 100644 index 00000000..14365e8e --- /dev/null +++ b/pr/src/md/unix/scoos.c @@ -0,0 +1,154 @@ +/* -*- 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. + */ + +/* + * SCO ODT 5.0 - originally created by mikep + */ +#include "primpl.h" + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +/* + * These are also implemented in pratom.c using NSPR locks. Any reason + * this might be better or worse? If you like this better, define + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h + */ +#ifdef _PR_HAVE_ATOMIC_OPS +/* Atomic operations */ +#include <stdio.h> +static FILE *_uw_semf; + +void +_MD_INIT_ATOMIC(void) +{ + /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ + if ((_uw_semf = tmpfile()) == NULL) + PR_ASSERT(0); + + return; +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)++; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)--; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + flockfile(_uw_semf); + *val = newval; + unflockfile(_uw_semf); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for SCO */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for SCO."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SCO."); +} diff --git a/pr/src/md/unix/solaris.c b/pr/src/md/unix/solaris.c new file mode 100644 index 00000000..8658ebbb --- /dev/null +++ b/pr/src/md/unix/solaris.c @@ -0,0 +1,808 @@ +/* -*- 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" + + +extern PRBool suspendAllOn; +extern PRThread *suspendAllThread; + +extern void _MD_SET_PRIORITY(_MDThread *md, PRThreadPriority newPri); + +PRIntervalTime _MD_Solaris_TicksPerSecond(void) +{ + /* + * Ticks have a 10-microsecond resolution. So there are + * 100000 ticks per second. + */ + return 100000UL; +} + +/* Interval timers, implemented using gethrtime() */ + +PRIntervalTime _MD_Solaris_GetInterval(void) +{ + union { + hrtime_t hrt; /* hrtime_t is a 64-bit (long long) integer */ + PRInt64 pr64; + } time; + PRInt64 resolution; + PRIntervalTime ticks; + + time.hrt = gethrtime(); /* in nanoseconds */ + /* + * Convert from nanoseconds to ticks. A tick's resolution is + * 10 microseconds, or 10000 nanoseconds. + */ + LL_I2L(resolution, 10000); + LL_DIV(time.pr64, time.pr64, resolution); + LL_L2UI(ticks, time.pr64); + return ticks; +} + +#ifdef _PR_PTHREADS +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np) +{ + *np = 0; + return NULL; +} +#endif /* _PR_PTHREADS */ + +#if defined(_PR_HAVE_ATOMIC_OPS) +/* NOTE: + * SPARC v9 (Ultras) do have an atomic test-and-set operation. But + * SPARC v8 doesn't. We should detect in the init if we are running on + * v8 or v9, and then use assembly where we can. + * + * This code uses the Solaris threads API. It can be used in both the + * pthreads and Solaris threads versions of nspr20 because "POSIX threads + * and Solaris threads are fully compatible even within the same process", + * to quote from pthread_create(3T). + */ + +#include <thread.h> +#include <synch.h> + +static mutex_t _solaris_atomic = DEFAULTMUTEX; + +PRInt32 +_MD_AtomicIncrement(PRInt32 *val) +{ + PRInt32 rv; + if (mutex_lock(&_solaris_atomic) != 0) + PR_ASSERT(0); + + rv = ++(*val); + + if (mutex_unlock(&_solaris_atomic) != 0)\ + PR_ASSERT(0); + + return rv; +} + +PRInt32 +_MD_AtomicDecrement(PRInt32 *val) +{ + PRInt32 rv; + if (mutex_lock(&_solaris_atomic) != 0) + PR_ASSERT(0); + + rv = --(*val); + + if (mutex_unlock(&_solaris_atomic) != 0)\ + PR_ASSERT(0); + + return rv; +} + +PRInt32 +_MD_AtomicSet(PRInt32 *val, PRInt32 newval) +{ + PRInt32 rv; + if (mutex_lock(&_solaris_atomic) != 0) + PR_ASSERT(0); + + rv = *val; + *val = newval; + + if (mutex_unlock(&_solaris_atomic) != 0)\ + PR_ASSERT(0); + + return rv; +} +#endif /* _PR_HAVE_ATOMIC_OPS */ + +#if defined(_PR_GLOBAL_THREADS_ONLY) +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <thread.h> + +#include <sys/lwp.h> +#include <sys/procfs.h> +#include <sys/syscall.h> +extern int syscall(); /* not declared in sys/syscall.h */ + +static sigset_t old_mask; /* store away original gc thread sigmask */ +static PRIntn gcprio; /* store away original gc thread priority */ + +THREAD_KEY_T threadid_key; +THREAD_KEY_T cpuid_key; +THREAD_KEY_T last_thread_key; +static sigset_t set, oldset; + +void _MD_EarlyInit(void) +{ + THR_KEYCREATE(&threadid_key, NULL); + THR_KEYCREATE(&cpuid_key, NULL); + THR_KEYCREATE(&last_thread_key, NULL); + sigemptyset(&set); + sigaddset(&set, SIGALRM); +} + +PRStatus _MD_CreateThread(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PRInt32 flags; + + /* mask out SIGALRM for native thread creation */ + thr_sigsetmask(SIG_BLOCK, &set, &oldset); + + flags = (state == PR_JOINABLE_THREAD) ? + THR_SUSPENDED : THR_SUSPENDED|THR_DETACHED; + if (thread->flags & (_PR_GCABLE_THREAD|_PR_BOUND_THREAD)) + flags |= THR_BOUND; + + if (thr_create(NULL, thread->stack->stackSize, + (void *(*)(void *)) start, (void *) thread, + flags, + &thread->md.handle)) { + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + return PR_FAILURE; + } + + /* When the thread starts running, then the lwpid is set to the right + * value. Until then we want to mark this as 'uninit' so that + * its register state is initialized properly for GC */ + + thread->md.lwpid = -1; + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + + if (scope == PR_GLOBAL_THREAD) { + thread->flags |= _PR_GLOBAL_SCOPE; + } + + _MD_SET_PRIORITY(&(thread->md), priority); + + /* Activate the thread */ + if (thr_continue( thread->md.handle ) ) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void _MD_cleanup_thread(PRThread *thread) +{ + thread_t hdl; + + hdl = thread->md.handle; + + /* + ** First, suspend the thread (unless it's the active one) + ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to + ** prevent both of us modifying the thread structure at the same time. + */ + if ( thread != _PR_MD_CURRENT_THREAD() ) { + thr_suspend(hdl); + } + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("(0X%x)[DestroyThread]\n", thread)); + + _MD_DESTROY_SEM(&thread->md.waiter_sem); +} + +void _MD_exit_thread(PRThread *thread) +{ + _MD_CLEAN_THREAD(thread); + _MD_SET_CURRENT_THREAD(NULL); +} + +void _MD_SET_PRIORITY(_MDThread *md_thread, + PRThreadPriority newPri) +{ + PRIntn nativePri; + + if (newPri < PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } else if (newPri > PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } + /* Solaris priorities are from 0 to 127 */ + nativePri = newPri * 127 / PR_PRIORITY_LAST; + if(thr_setprio((thread_t)md_thread->handle, nativePri)) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("_PR_SetThreadPriority: can't set thread priority\n")); + } +} + +void _MD_WAIT_CV( + struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout) +{ + struct timespec tt; + PRUint32 msec; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT((!suspendAllOn) || (suspendAllThread != me)); + + if (PR_INTERVAL_NO_TIMEOUT == timeout) { + COND_WAIT(&md_cv->cv, &md_lock->lock); + } else { + msec = PR_IntervalToMilliseconds(timeout); + + GETTIME(&tt); + tt.tv_sec += msec / PR_MSEC_PER_SEC; + tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC; + /* Check for nsec overflow - otherwise we'll get an EINVAL */ + if (tt.tv_nsec >= PR_NSEC_PER_SEC) { + tt.tv_sec++; + tt.tv_nsec -= PR_NSEC_PER_SEC; + } + COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt); + } +} + +void _MD_lock(struct _MDLock *md_lock) +{ +#ifdef DEBUG + /* This code was used for GC testing to make sure that we didn't attempt + * to grab any locks while threads are suspended. + */ + PRLock *lock; + + if ((suspendAllOn) && (suspendAllThread == _PR_MD_CURRENT_THREAD())) { + lock = ((PRLock *) ((char*) (md_lock) - offsetof(PRLock,ilock))); + PR_ASSERT(lock->owner == NULL); + return; + } +#endif /* DEBUG */ + + mutex_lock(&md_lock->lock); +} + +PRThread *_pr_current_thread_tls() +{ + PRThread *ret; + + thr_getspecific(threadid_key, (void **)&ret); + return ret; +} + +PRStatus +_MD_wait(PRThread *thread, PRIntervalTime ticks) +{ + _MD_WAIT_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +PRStatus +_MD_WakeupWaiter(PRThread *thread) +{ + if (thread == NULL) { + return PR_SUCCESS; + } + _MD_POST_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +_PRCPU *_pr_current_cpu_tls() +{ + _PRCPU *ret; + + thr_getspecific(cpuid_key, (void **)&ret); + return ret; +} + +PRThread *_pr_last_thread_tls() +{ + PRThread *ret; + + thr_getspecific(last_thread_key, (void **)&ret); + return ret; +} + +_MDLock _pr_ioq_lock; + +void +_MD_InitIO(void) +{ + _MD_NEW_LOCK(&_pr_ioq_lock); +} + +PRStatus _MD_InitializeThread(PRThread *thread) +{ + if (!_PR_IS_NATIVE_THREAD(thread)) + return PR_SUCCESS; + /* sol_curthread is an asm routine which grabs GR7; GR7 stores an internal + * thread structure ptr used by solaris. We'll use this ptr later + * with suspend/resume to find which threads are running on LWPs. + */ + thread->md.threadID = sol_curthread(); + /* prime the sp; substract 4 so we don't hit the assert that + * curr sp > base_stack + */ + thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long); + thread->md.lwpid = _lwp_self(); + thread->md.handle = THR_SELF(); + + /* all threads on Solaris are global threads from NSPR's perspective + * since all of them are mapped to Solaris threads. + */ + thread->flags |= _PR_GLOBAL_SCOPE; + + /* For primordial/attached thread, we don't create an underlying native thread. + * So, _MD_CREATE_THREAD() does not get called. We need to do initialization + * like allocating thread's synchronization variables and set the underlying + * native thread's priority. + */ + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + _MD_SET_PRIORITY(&(thread->md), thread->priority); + } + return PR_SUCCESS; +} + +/* Sleep for n milliseconds, n < 1000 */ +void solaris_msec_sleep(int n) +{ + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1000000*n; + if (syscall(SYS_nanosleep, &ts, 0, 0) < 0) { + PR_ASSERT(0); + } +} + +#define VALID_SP(sp, bottom, top) \ + (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top))) + +void solaris_record_regs(PRThread *t, prstatus_t *lwpstatus) +{ +#ifdef sparc + long *regs = (long *)&t->md.context.uc_mcontext.gregs[0]; + + PR_ASSERT(t->flags & _PR_GCABLE_THREAD); + PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[REG_G7]); + + t->md.sp = lwpstatus->pr_reg[REG_SP]; + PR_ASSERT(VALID_SP(t->md.sp, t->stack->stackBottom, t->stack->stackTop)); + + regs[0] = lwpstatus->pr_reg[R_G1]; + regs[1] = lwpstatus->pr_reg[R_G2]; + regs[2] = lwpstatus->pr_reg[R_G3]; + regs[3] = lwpstatus->pr_reg[R_G4]; + regs[4] = lwpstatus->pr_reg[R_O0]; + regs[5] = lwpstatus->pr_reg[R_O1]; + regs[6] = lwpstatus->pr_reg[R_O2]; + regs[7] = lwpstatus->pr_reg[R_O3]; + regs[8] = lwpstatus->pr_reg[R_O4]; + regs[9] = lwpstatus->pr_reg[R_O5]; + regs[10] = lwpstatus->pr_reg[R_O6]; + regs[11] = lwpstatus->pr_reg[R_O7]; +#elif defined(i386) + /* + * To be implemented and tested + */ + PR_ASSERT(0); + PR_ASSERT(t->md.threadID == lwpstatus->pr_reg[GS]); + t->md.sp = lwpstatus->pr_reg[UESP]; +#endif +} + +void solaris_preempt_off() +{ + sigset_t set; + + (void)sigfillset(&set); + syscall(SYS_sigprocmask, SIG_SETMASK, &set, &old_mask); +} + +void solaris_preempt_on() +{ + syscall(SYS_sigprocmask, SIG_SETMASK, &old_mask, NULL); +} + +int solaris_open_main_proc_fd() +{ + char buf[30]; + int fd; + + /* Not locked, so must be created while threads coming up */ + PR_snprintf(buf, sizeof(buf), "/proc/%ld", getpid()); + if ( (fd = syscall(SYS_open, buf, O_RDONLY)) < 0) { + return -1; + } + return fd; +} + +/* Return a file descriptor for the /proc entry corresponding to the + * given lwp. + */ +int solaris_open_lwp(lwpid_t id, int lwp_main_proc_fd) +{ + int result; + + if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCOPENLWP, &id)) <0) + return -1; /* exited??? */ + + return result; +} +void _MD_Begin_SuspendAll() +{ + solaris_preempt_off(); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n")); + /* run at highest prio so I cannot be preempted */ + thr_getprio(thr_self(), &gcprio); + thr_setprio(thr_self(), 0x7fffffff); + suspendAllOn = PR_TRUE; + suspendAllThread = _PR_MD_CURRENT_THREAD(); +} + +void _MD_End_SuspendAll() +{ +} + +void _MD_End_ResumeAll() +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n")); + thr_setprio(thr_self(), gcprio); + solaris_preempt_on(); + suspendAllThread = NULL; + suspendAllOn = PR_FALSE; +} + +void _MD_Suspend(PRThread *thr) +{ + int lwp_fd, result; + prstatus_t lwpstatus; + int lwp_main_proc_fd = 0; + + if (!(thr->flags & _PR_GCABLE_THREAD) || !suspendAllOn){ + /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend + * during that time we can't call any thread lib or libc calls. Hence + * make sure that no suspension is requested for Non gcable thread + * during suspendAllOn */ + PR_ASSERT(!suspendAllOn); + thr_suspend(thr->md.handle); + return; + } + + /* XXX Primordial thread can't be bound to an lwp, hence there is no + * way we can assume that we can get the lwp status for primordial + * thread reliably. Hence we skip this for primordial thread, hoping + * that the SP is saved during lock and cond. wait. + * XXX - Again this is concern only for java interpreter, not for the + * server, 'cause primordial thread in the server does not do java work + */ + if (thr->flags & _PR_PRIMORDIAL) + return; + + /* XXX Important Note: If the start function of a thread is not called, + * lwpid is -1. Then, skip this thread. This thread will get caught + * in PR_NativeRunThread before calling the start function, because + * we hold the pr_activeLock during suspend/resume */ + + /* if the thread is not started yet then don't do anything */ + if (!suspendAllOn || thr->md.lwpid == -1) + return; + + if (_lwp_suspend(thr->md.lwpid) < 0) { + PR_ASSERT(0); + return; + } + + if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) { + PR_ASSERT(0); + return; /* XXXMB ARGH, we're hosed! */ + } + + if ( (lwp_fd = solaris_open_lwp(thr->md.lwpid, lwp_main_proc_fd)) < 0) { + PR_ASSERT(0); + close(lwp_main_proc_fd); + return; + } + if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) { + /* Hopefully the thread just died... */ + close(lwp_fd); + close(lwp_main_proc_fd); + return; + } + while ( !(lwpstatus.pr_flags & PR_STOPPED) ) { + if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) { + PR_ASSERT(0); /* ARGH SOMETHING WRONG! */ + break; + } + solaris_msec_sleep(1); + } + solaris_record_regs(thr, &lwpstatus); + close(lwp_fd); + close(lwp_main_proc_fd); +} + +#ifdef OLD_CODE + +void _MD_SuspendAll() +{ + /* On solaris there are threads, and there are LWPs. + * Calling _PR_DoSingleThread would freeze all of the threads bound to LWPs + * but not necessarily stop all LWPs (for example if someone did + * an attachthread of a thread which was not bound to an LWP). + * So now go through all the LWPs for this process and freeze them. + * + * Note that if any thread which is capable of having the GC run on it must + * had better be a LWP with a single bound thread on it. Otherwise, this + * might not stop that thread from being run. + */ + PRThread *current = _PR_MD_CURRENT_THREAD(); + prstatus_t status, lwpstatus; + int result, index, lwp_fd; + lwpid_t me = _lwp_self(); + int err; + int lwp_main_proc_fd; + + solaris_preempt_off(); + + /* run at highest prio so I cannot be preempted */ + thr_getprio(thr_self(), &gcprio); + thr_setprio(thr_self(), 0x7fffffff); + + current->md.sp = (uint_t)&me; /* set my own stack pointer */ + + if ( (lwp_main_proc_fd = solaris_open_main_proc_fd()) < 0) { + PR_ASSERT(0); + solaris_preempt_on(); + return; /* XXXMB ARGH, we're hosed! */ + } + + if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCSTATUS, &status)) < 0) { + err = errno; + PR_ASSERT(0); + goto failure; /* XXXMB ARGH, we're hosed! */ + } + + num_lwps = status.pr_nlwp; + + if ( (all_lwps = (lwpid_t *)PR_MALLOC((num_lwps+1) * sizeof(lwpid_t)))==NULL) { + PR_ASSERT(0); + goto failure; /* XXXMB ARGH, we're hosed! */ + } + + if ( (result = syscall(SYS_ioctl, lwp_main_proc_fd, PIOCLWPIDS, all_lwps)) < 0) { + PR_ASSERT(0); + PR_DELETE(all_lwps); + goto failure; /* XXXMB ARGH, we're hosed! */ + } + + for (index=0; index< num_lwps; index++) { + if (all_lwps[index] != me) { + if (_lwp_suspend(all_lwps[index]) < 0) { + /* could happen if lwp exited */ + all_lwps[index] = me; /* dummy it up */ + } + } + } + + /* Turns out that lwp_suspend is not a blocking call. + * Go through the list and make sure they are all stopped. + */ + for (index=0; index< num_lwps; index++) { + if (all_lwps[index] != me) { + if ( (lwp_fd = solaris_open_lwp(all_lwps[index], lwp_main_proc_fd)) < 0) { + PR_ASSERT(0); + PR_DELETE(all_lwps); + all_lwps = NULL; + goto failure; /* XXXMB ARGH, we're hosed! */ + } + + if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) { + /* Hopefully the thread just died... */ + close(lwp_fd); + continue; + } + while ( !(lwpstatus.pr_flags & PR_STOPPED) ) { + if ( (result = syscall(SYS_ioctl, lwp_fd, PIOCSTATUS, &lwpstatus)) < 0) { + PR_ASSERT(0); /* ARGH SOMETHING WRONG! */ + break; + } + solaris_msec_sleep(1); + } + solaris_record_regs(&lwpstatus); + close(lwp_fd); + } + } + + close(lwp_main_proc_fd); + + return; +failure: + solaris_preempt_on(); + thr_setprio(thr_self(), gcprio); + close(lwp_main_proc_fd); + return; +} + +void _MD_ResumeAll() +{ + int i; + lwpid_t me = _lwp_self(); + + for (i=0; i < num_lwps; i++) { + if (all_lwps[i] == me) + continue; + if ( _lwp_continue(all_lwps[i]) < 0) { + PR_ASSERT(0); /* ARGH, we are hosed! */ + } + } + + /* restore priority and sigmask */ + thr_setprio(thr_self(), gcprio); + solaris_preempt_on(); + PR_DELETE(all_lwps); + all_lwps = NULL; +} +#endif /* OLD_CODE */ + +#ifdef USE_SETJMP +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} +#else +PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np) +{ + if (isCurrent) { + (void) getcontext(CONTEXT(t)); + } + *np = NGREG; + return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; +} +#endif /* USE_SETJMP */ + +#else /* _PR_GLOBAL_THREADS_ONLY */ + +#if defined(_PR_LOCAL_THREADS_ONLY) + +void _MD_EarlyInit(void) +{ +} + +void _MD_SolarisInit() +{ + _PR_UnixInit(); +} + +void +_MD_SET_PRIORITY(_MDThread *thread, PRThreadPriority newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + PR_ASSERT((thread == NULL) || (!(thread->flags & _PR_GLOBAL_SCOPE))); + return PR_SUCCESS; +} + +/* These functions should not be called for Solaris */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Solaris"); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Solaris"); + return(PR_FAILURE); +} + +#ifdef USE_SETJMP +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} +#else +PRWord *_MD_HomeGCRegisters(PRThread *t, PRIntn isCurrent, PRIntn *np) +{ + if (isCurrent) { + (void) getcontext(CONTEXT(t)); + } + *np = NGREG; + return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; +} +#endif /* USE_SETJMP */ + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +#endif /* _PR_GLOBAL_THREADS_ONLY */ + +#ifndef _PR_PTHREADS +#if defined(i386) && defined(SOLARIS2_4) +/* + * Because clock_gettime() on Solaris/x86 2.4 always generates a + * segmentation fault, we use an emulated version _pr_solx86_clock_gettime(), + * which is implemented using gettimeofday(). + */ + +int +_pr_solx86_clock_gettime(clockid_t clock_id, struct timespec *tp) +{ + struct timeval tv; + + if (clock_id != CLOCK_REALTIME) { + errno = EINVAL; + return -1; + } + + gettimeofday(&tv); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; +} +#endif /* i386 && SOLARIS2_4 */ +#endif /* _PR_PTHREADS */ diff --git a/pr/src/md/unix/sony.c b/pr/src/md/unix/sony.c new file mode 100644 index 00000000..d3ce90c4 --- /dev/null +++ b/pr/src/md/unix/sony.c @@ -0,0 +1,90 @@ +/* -*- 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 <signal.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ +#ifndef _PR_PTHREADS + if (isCurrent) { + (void) setjmp(CONTEXT(t), 1); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +#else + *np = 0; + return NULL; +#endif +} + +#ifndef _PR_PTHREADS +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Sony */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for SONY."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SONY."); + return PR_FAILURE; +} +#endif /* ! _PR_PTHREADS */ diff --git a/pr/src/md/unix/sunos4.c b/pr/src/md/unix/sunos4.c new file mode 100644 index 00000000..0b4b9b4f --- /dev/null +++ b/pr/src/md/unix/sunos4.c @@ -0,0 +1,77 @@ +/* -*- 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 <setjmp.h> +#include "primpl.h" + +void _MD_EarlyInit(void) +{ +} + +PRStatus _MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for SunOS 4.1.3."); + return PR_FAILURE; +} + +void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri) +{ + PR_NOT_REACHED("_MD_SET_PRIORITY should not be called for user-level threads."); +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +PRStatus _MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for SunOS 4.1.3."); +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} diff --git a/pr/src/md/unix/unix.c b/pr/src/md/unix/unix.c new file mode 100644 index 00000000..4148fb4d --- /dev/null +++ b/pr/src/md/unix/unix.c @@ -0,0 +1,3210 @@ +/* -*- 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 <signal.h> +#include <unistd.h> +#include <memory.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +/* To get FIONREAD */ +#if defined(NCR) || defined(UNIXWARE) || defined(NEC) || defined(SNI) \ + || defined(SONY) +#include <sys/filio.h> +#endif + +/* + * Make sure _PRSockLen_t is 32-bit, because we will cast a PRUint32* or + * PRInt32* pointer to a _PRSockLen_t* pointer. + */ +#if defined(IRIX) || defined(HPUX) || defined(OSF1) || defined(SOLARIS) \ + || defined(AIX4_1) || defined(LINUX) || defined(SONY) \ + || defined(BSDI) || defined(SCO) || defined(NEC) || defined(SNI) \ + || defined(SUNOS4) +#define _PRSockLen_t int +#elif (defined(AIX) && !defined(AIX4_1)) || defined(FREEBSD) \ + || defined(UNIXWARE) +#define _PRSockLen_t size_t +#else +#error "Cannot determine architecture" +#endif + +/* +** Global lock variable used to bracket calls into rusty libraries that +** aren't thread safe (like libc, libX, etc). +*/ +static PRLock *_pr_rename_lock = NULL; +static PRMonitor *_pr_Xfe_mon = NULL; + +/* + * Variables used by the GC code, initialized in _MD_InitSegs(). + * _pr_zero_fd should be a static variable. Unfortunately, there is + * still some Unix-specific code left in function PR_GrowSegment() + * in file memory/prseg.c that references it, so it needs + * to be a global variable for now. + */ +PRInt32 _pr_zero_fd = -1; +static PRLock *_pr_md_lock = NULL; + +sigset_t timer_set; + +#if !defined(_PR_PTHREADS) + +static sigset_t empty_set; + +#ifdef SOLARIS +#include <sys/file.h> +#include <sys/filio.h> +#endif + +#ifndef PIPE_BUF +#define PIPE_BUF 512 +#endif + +#ifndef PROT_NONE +#define PROT_NONE 0 +#endif + +/* + * _nspr_noclock - if set clock interrupts are disabled + */ +int _nspr_noclock = 0; + +#ifdef IRIX +extern PRInt32 _nspr_terminate_on_error; +#endif + +/* + * There is an assertion in this code that NSPR's definition of PRIOVec + * is bit compatible with UNIX' definition of a struct iovec. This is + * applicable to the 'writev()' operations where the types are casually + * cast to avoid warnings. + */ + +int _pr_md_pipefd[2] = { -1, -1 }; +static char _pr_md_pipebuf[PIPE_BUF]; + +_PRInterruptTable _pr_interruptTable[] = { + { + "clock", _PR_MISSED_CLOCK, _PR_ClockInterrupt, }, + { + 0 } +}; + +PR_IMPLEMENT(void) _MD_unix_init_running_cpu(_PRCPU *cpu) +{ + PR_INIT_CLIST(&(cpu->md.md_unix.ioQ)); + cpu->md.md_unix.ioq_max_osfd = -1; + cpu->md.md_unix.ioq_timeout = PR_INTERVAL_NO_TIMEOUT; +} + +PRStatus _MD_open_dir(_MDDir *d, const char *name) +{ +int err; + + d->d = opendir(name); + if (!d->d) { + err = _MD_ERRNO(); + _PR_MD_MAP_OPENDIR_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRInt32 _MD_close_dir(_MDDir *d) +{ +int rv = 0, err; + + if (d->d) { + rv = closedir(d->d); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_CLOSEDIR_ERROR(err); + } + } + return rv; +} + +char * _MD_read_dir(_MDDir *d, PRIntn flags) +{ +struct dirent *de; +int err; + + for (;;) { + /* + * XXX: readdir() is not MT-safe. There is an MT-safe version + * readdir_r() on some systems. + */ + de = readdir(d->d); + if (!de) { + err = _MD_ERRNO(); + _PR_MD_MAP_READDIR_ERROR(err); + 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; + if ((flags & PR_SKIP_HIDDEN) && (de->d_name[0] == '.')) + continue; + break; + } + return de->d_name; +} + +PRInt32 _MD_delete(const char *name) +{ +PRInt32 rv, err; +#ifdef UNIXWARE + sigset_t set, oset; +#endif + +#ifdef UNIXWARE + sigfillset(&set); + sigprocmask(SIG_SETMASK, &set, &oset); +#endif + rv = unlink(name); +#ifdef UNIXWARE + sigprocmask(SIG_SETMASK, &oset, NULL); +#endif + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_UNLINK_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_getfileinfo(const char *fn, PRFileInfo *info) +{ + struct stat sb; + PRInt64 s, s2us; + PRInt32 rv, err; + + rv = stat(fn, &sb); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_STAT_ERROR(err); + } else 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(s, sb.st_mtime); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; + } + return rv; +} + +PRInt32 _MD_getfileinfo64(const char *fn, PRFileInfo64 *info) +{ + PRFileInfo info32; + PRInt32 rv = _MD_getfileinfo(fn, &info32); + if (rv >= 0) + { + info->type = info32.type; + LL_I2L(info->size, info32.size); + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + } + return rv; +} + +PRInt32 _MD_getopenfileinfo(const PRFileDesc *fd, PRFileInfo *info) +{ + struct stat sb; + PRInt64 s, s2us; + PRInt32 rv, err; + + rv = fstat(fd->secret->md.osfd, &sb); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_FSTAT_ERROR(err); + } else if (info) { + 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(s, sb.st_mtime); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; + } + } + return rv; +} + +PRInt32 _MD_getopenfileinfo64(const PRFileDesc *fd, PRFileInfo64 *info) +{ + PRFileInfo info32; + PRInt32 rv = _MD_getopenfileinfo(fd, &info32); + if (rv >= 0) + { + info->type = info32.type; + LL_I2L(info->size, info32.size); + info->modifyTime = info32.modifyTime; + info->creationTime = info32.creationTime; + } + return rv; +} + +PRInt32 _MD_rename(const char *from, const char *to) +{ + PRInt32 rv = -1, err; + + /* + ** This is trying to enforce the semantics of WINDOZE' rename + ** operation. That means one is not allowed to rename over top + ** of an existing file. Holding a lock across these two function + ** and the open function is known to be a bad idea, but .... + */ + if (NULL != _pr_rename_lock) + PR_Lock(_pr_rename_lock); + if (0 == access(to, F_OK)) + PR_SetError(PR_FILE_EXISTS_ERROR, 0); + else + { + rv = rename(from, to); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_RENAME_ERROR(err); + } + } + if (NULL != _pr_rename_lock) + PR_Unlock(_pr_rename_lock); + return rv; +} + +PRInt32 _MD_access(const char *name, PRIntn how) +{ +PRInt32 rv, err; +int amode; + + switch (how) { + case PR_ACCESS_WRITE_OK: + amode = W_OK; + break; + case PR_ACCESS_READ_OK: + amode = R_OK; + break; + case PR_ACCESS_EXISTS: + amode = F_OK; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = -1; + goto done; + } + rv = access(name, amode); + + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_ACCESS_ERROR(err); + } + +done: + return(rv); +} + +PRInt32 _MD_mkdir(const char *name, PRIntn mode) +{ +int rv, err; + + /* + ** This lock is used to enforce rename semantics as described + ** in PR_Rename. Look there for more fun details. + */ + if (NULL !=_pr_rename_lock) + PR_Lock(_pr_rename_lock); + rv = mkdir(name, mode); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_MKDIR_ERROR(err); + } + if (NULL !=_pr_rename_lock) + PR_Unlock(_pr_rename_lock); + return rv; +} + +PRInt32 _MD_rmdir(const char *name) +{ +int rv, err; + + rv = rmdir(name); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_RMDIR_ERROR(err); + } + return rv; +} + +PRInt32 _MD_read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ +PRThread *me = _PR_MD_CURRENT_THREAD(); +PRInt32 rv, err; +fd_set rd; +PRInt32 osfd = fd->secret->md.osfd; + + FD_ZERO(&rd); + FD_SET(osfd, &rd); + while ((rv = read(osfd,buf,amount)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_WaitForFD(osfd, PR_POLL_READ, PR_INTERVAL_NO_TIMEOUT); + } else { + while ((rv = _MD_SELECT(osfd + 1, &rd, NULL, NULL, NULL)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_SELECT() if it is interrupted */ + } + if (rv == -1) { + break; + } + } + if (_PR_PENDING_INTERRUPT(me)) + break; + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + _PR_MD_MAP_READ_ERROR(err); + } + } + return(rv); +} + +PRInt32 _MD_write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ +PRThread *me = _PR_MD_CURRENT_THREAD(); +PRInt32 rv, err; +fd_set wd; +PRInt32 osfd = fd->secret->md.osfd; + + FD_ZERO(&wd); + FD_SET(osfd, &wd); + while ((rv = write(osfd,buf,amount)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + _PR_WaitForFD(osfd, PR_POLL_WRITE, PR_INTERVAL_NO_TIMEOUT); + } else { + while ((rv = _MD_SELECT(osfd + 1, NULL, &wd, NULL, NULL)) + == -1 && (err = _MD_ERRNO()) == EINTR) { + /* retry _MD_SELECT() if it is interrupted */ + } + if (rv == -1) { + break; + } + } + if (_PR_PENDING_INTERRUPT(me)) + break; + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + } else { + _PR_MD_MAP_WRITE_ERROR(err); + } + } + return(rv); +} + +PRInt32 _MD_lseek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ +PRInt32 rv, where, err; + + switch (whence) { + case PR_SEEK_SET: + where = SEEK_SET; + break; + case PR_SEEK_CUR: + where = SEEK_CUR; + break; + case PR_SEEK_END: + where = SEEK_END; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = -1; + goto done; + } + rv = lseek(fd->secret->md.osfd,offset,where); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_LSEEK_ERROR(err); + } +done: + return(rv); +} + +PRInt64 _MD_lseek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + PRInt32 off, rv; + PRInt64 on, result = LL_MININT; + LL_L2I(off, offset); + LL_I2L(on, off); + if (LL_EQ(offset, on)) + { + rv = _MD_lseek(fd, off, whence); + if (rv >= 0) LL_I2L(result, rv); + } + else PR_SetError(PR_FILE_TOO_BIG_ERROR, 0); /* overflow */ + return result; +} /* _MD_lseek64 */ + +PRInt32 _MD_fsync(PRFileDesc *fd) +{ +PRInt32 rv, err; + + rv = fsync(fd->secret->md.osfd); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_FSYNC_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_close(PRInt32 osfd) +{ +PRInt32 rv, err; + + rv = close(osfd); + if (rv == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_CLOSE_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRInt32 osfd, err; + + osfd = socket(domain, type, proto); + + if (osfd == -1) { + err = _MD_ERRNO(); + _PR_MD_MAP_SOCKET_ERROR(err); + return(osfd); + } + + return(osfd); +} + +PRInt32 _MD_socketavailable(PRFileDesc *fd) +{ + PRInt32 result; + + if (ioctl(fd->secret->md.osfd, FIONREAD, &result) < 0) { + _PR_MD_MAP_SOCKETAVAILABLE_ERROR(_MD_ERRNO()); + return -1; + } + return result; +} + +PRInt64 _MD_socketavailable64(PRFileDesc *fd) +{ + PRInt64 result; + LL_I2L(result, _MD_socketavailable(fd)); + return result; +} /* _MD_socketavailable64 */ + +#define READ_FD 1 +#define WRITE_FD 2 + +/* + * wait for socket i/o, periodically checking for interrupt + */ + +static PRInt32 socket_io_wait(PRInt32 osfd, PRInt32 fd_type, + PRIntervalTime timeout) +{ + PRInt32 rv = -1; + struct timeval tv, *tvp; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntervalTime epoch, now, elapsed, remaining; + PRInt32 syserror; + fd_set rd_wr; + + switch (timeout) { + case PR_INTERVAL_NO_WAIT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + break; + case PR_INTERVAL_NO_TIMEOUT: + /* + * This is a special case of the 'default' case below. + * Please see the comments there. + */ + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + tvp = &tv; + FD_ZERO(&rd_wr); + do { + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) + rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, tvp); + else + rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, tvp); + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + if (syserror == EBADF) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF); + } else { + PR_SetError(PR_UNKNOWN_ERROR, syserror); + } + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + default: + now = epoch = PR_IntervalNow(); + remaining = timeout; + tvp = &tv; + FD_ZERO(&rd_wr); + do { + /* + * We block in _MD_SELECT for at most + * _PR_INTERRUPT_CHECK_INTERVAL_SECS seconds, + * so that there is an upper limit on the delay + * before the interrupt bit is checked. + */ + tv.tv_sec = PR_IntervalToSeconds(remaining); + if (tv.tv_sec > _PR_INTERRUPT_CHECK_INTERVAL_SECS) { + tv.tv_sec = _PR_INTERRUPT_CHECK_INTERVAL_SECS; + tv.tv_usec = 0; + } else { + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - + PR_SecondsToInterval(tv.tv_sec)); + } + FD_SET(osfd, &rd_wr); + if (fd_type == READ_FD) + rv = _MD_SELECT(osfd + 1, &rd_wr, NULL, NULL, tvp); + else + rv = _MD_SELECT(osfd + 1, NULL, &rd_wr, NULL, tvp); + /* + * we don't consider EINTR a real error + */ + if (rv == -1 && (syserror = _MD_ERRNO()) != EINTR) { + if (syserror == EBADF) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, EBADF); + } else { + PR_SetError(PR_UNKNOWN_ERROR, syserror); + } + break; + } + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + rv = -1; + break; + } + /* + * We loop again if _MD_SELECT timed out or got interrupted + * by a signal, and the timeout deadline has not passed yet. + */ + if (rv == 0 || (rv == -1 && syserror == EINTR)) { + /* + * If _MD_SELECT timed out, we know how much time + * we spent in blocking, so we can avoid a + * PR_IntervalNow() call. + */ + if (rv == 0) { + now += PR_SecondsToInterval(tv.tv_sec) + + PR_MicrosecondsToInterval(tv.tv_usec); + } else { + now = PR_IntervalNow(); + } + elapsed = (PRIntervalTime) (now - epoch); + if (elapsed >= timeout) { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + rv = -1; + break; + } else { + remaining = timeout - elapsed; + } + } + } while (rv == 0 || (rv == -1 && syserror == EINTR)); + break; + } + return(rv); +} + +PRInt32 _MD_recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRInt32 flags, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + +/* + * Many OS's (Solaris, Unixware) have a broken recv which won't read + * from socketpairs. As long as we don't use flags on socketpairs, this + * is a decent fix. - mikep + */ +#if defined(UNIXWARE) || defined(SOLARIS) || defined(NCR) + while ((rv = read(osfd,buf,amount)) == -1) { +/* + while ((rv = recv(osfd,buf,amount,flags)) == -1) { +*/ +#else + while ((rv = recv(osfd,buf,amount,flags)) == -1) { +#endif + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _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; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((*addrlen = PR_NETADDR_SIZE(addr)), + ((rv = recvfrom(osfd, buf, amount, flags, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen)) == -1)) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_RECVFROM_ERROR(err); + } +done: +#ifdef AIX + if (rv != -1) { + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) { + addr->inet.family &= 0x00ff; + } + } +#endif + return(rv); +} + +PRInt32 _MD_send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRInt32 flags, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = send(osfd,buf,amount,flags)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next send() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) { + if (_PR_IS_NATIVE_THREAD(me)) { + if (socket_io_wait(osfd, WRITE_FD, timeout)< 0) + goto done; + } else { + if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + goto done; + } + } + } + if (rv < 0) { + _PR_MD_MAP_SEND_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _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; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = sendto(osfd, buf, amount, flags, + (struct sockaddr *) addr, addrlen)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, WRITE_FD, timeout))< 0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_SENDTO_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_writev(PRFileDesc *fd, PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 index, amount = 0; + PRInt32 osfd = fd->secret->md.osfd; + + /* + * Calculate the total number of bytes to be sent; needed for + * optimization later. + * We could avoid this if this number was passed in; but it is + * probably not a big deal because iov_size is usually small (less than + * 3) + */ + if (!fd->secret->nonblocking) { + for (index=0; index<iov_size; index++) { + amount += iov[index].iov_len; + } + } + + while ((rv = writev(osfd, (const struct iovec*)iov, iov_size)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, WRITE_FD, timeout))<0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + /* + * optimization; if bytes sent is less than "amount" call + * select before returning. This is because it is likely that + * the next writev() call will return EWOULDBLOCK. + */ + if ((!fd->secret->nonblocking) && (rv > 0) && (rv < amount) + && (timeout != PR_INTERVAL_NO_WAIT)) { + if (_PR_IS_NATIVE_THREAD(me)) { + if (socket_io_wait(osfd, WRITE_FD, timeout) < 0) + goto done; + } else { + if (_PR_WaitForFD(osfd, PR_POLL_WRITE, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + goto done; + } + } + } + if (rv < 0) { + _PR_MD_MAP_WRITEV_ERROR(err); + } +done: + return(rv); +} + +PRInt32 _MD_accept(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen, PRIntervalTime timeout) +{ + PRInt32 osfd = fd->secret->md.osfd; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + while ((rv = accept(osfd, (struct sockaddr *) addr, + (_PRSockLen_t *)addrlen)) == -1) { + err = _MD_ERRNO(); + if ((err == EAGAIN) || (err == EWOULDBLOCK)) { + if (fd->secret->nonblocking) { + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(osfd, PR_POLL_READ, timeout) == 0) { + rv = -1; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + } else + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + 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 ((rv = socket_io_wait(osfd, READ_FD, timeout)) < 0) + goto done; + } + } else if ((err == EINTR) && (!_PR_PENDING_INTERRUPT(me))){ + continue; + } else { + break; + } + } + if (rv < 0) { + _PR_MD_MAP_ACCEPT_ERROR(err); + } +done: +#ifdef AIX + if (rv != -1) { + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) { + addr->inet.family &= 0x00ff; + } + } +#endif + return(rv); +} + +extern int _connect (int s, const struct sockaddr *name, int namelen); +PRInt32 _MD_connect( + PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen, PRIntervalTime timeout) +{ + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRInt32 osfd = fd->secret->md.osfd; +#ifdef IRIX +extern PRInt32 _MD_irix_connect( + PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, PRIntervalTime timeout); +#endif + + /* + * We initiate the connection setup by making a nonblocking connect() + * call. If the connect() call fails, there are two cases we handle + * specially: + * 1. The connect() call was interrupted by a signal. In this case + * we simply retry connect(). + * 2. The NSPR socket is nonblocking and connect() fails with + * EINPROGRESS. We first wait until the socket becomes writable. + * Then we try to find out whether the connection setup succeeded + * or failed. + */ + +retry: +#ifdef IRIX + if ((rv = _MD_irix_connect(osfd, addr, addrlen, timeout)) == -1) { +#else + if ((rv = connect(osfd, (struct sockaddr *)addr, addrlen)) == -1) { +#endif + err = _MD_ERRNO(); + + if (err == EINTR) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + goto retry; + } + + if (!fd->secret->nonblocking && (err == EINPROGRESS)) { + if (!_PR_IS_NATIVE_THREAD(me)) { + /* + * _PR_WaitForFD() may return 0 (timeout or interrupt) or 1. + */ + + rv = _PR_WaitForFD(osfd, PR_POLL_WRITE, timeout); + if (rv == 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); + } + return -1; + } + } else { + /* + * socket_io_wait() may return -1 or 1. + */ + + rv = socket_io_wait(osfd, WRITE_FD, timeout); + if (rv == -1) { + return -1; + } + } + + PR_ASSERT(rv == 1); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return -1; + } + return 0; + } + + _PR_MD_MAP_CONNECT_ERROR(err); + } + + return rv; +} /* _MD_connect */ + +PRInt32 _MD_bind(PRFileDesc *fd, const PRNetAddr *addr, PRUint32 addrlen) +{ + PRInt32 rv, err; + + rv = bind(fd->secret->md.osfd, (struct sockaddr *) addr, (int )addrlen); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_BIND_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_listen(PRFileDesc *fd, PRIntn backlog) +{ + PRInt32 rv, err; + + rv = listen(fd->secret->md.osfd, backlog); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_LISTEN_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_shutdown(PRFileDesc *fd, PRIntn how) +{ + PRInt32 rv, err; + + rv = shutdown(fd->secret->md.osfd, how); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SHUTDOWN_ERROR(err); + } + return(rv); +} + +PRInt32 _MD_socketpair(int af, int type, int flags, + PRInt32 *osfd) +{ + PRInt32 rv, err; + + rv = socketpair(af, type, flags, osfd); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SOCKETPAIR_ERROR(err); + } + return rv; +} + +PRStatus _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getsockname(fd->secret->md.osfd, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); +#ifdef AIX + if (rv == 0) { + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) { + addr->inet.family &= 0x00ff; + } + } +#endif + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETSOCKNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, + PRUint32 *addrlen) +{ + PRInt32 rv, err; + + rv = getpeername(fd->secret->md.osfd, + (struct sockaddr *) addr, (_PRSockLen_t *)addrlen); +#ifdef AIX + if (rv == 0) { + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) { + addr->inet.family &= 0x00ff; + } + } +#endif + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETPEERNAME_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_getsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, char* optval, PRInt32* optlen) +{ + PRInt32 rv, err; + + rv = getsockopt(fd->secret->md.osfd, level, optname, optval, (_PRSockLen_t *)optlen); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_GETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PRStatus _MD_setsockopt(PRFileDesc *fd, PRInt32 level, + PRInt32 optname, const char* optval, PRInt32 optlen) +{ + PRInt32 rv, err; + + rv = setsockopt(fd->secret->md.osfd, level, optname, optval, optlen); + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_SETSOCKOPT_ERROR(err); + } + return rv==0?PR_SUCCESS:PR_FAILURE; +} + +PR_IMPLEMENT(PRInt32) _MD_pr_poll(PRPollDesc *pds, PRIntn npds, + PRIntervalTime timeout) +{ + PRPollDesc *pd, *epd; + PRPollQueue pq; + PRInt32 n, err, pdcnt; + PRIntn is; + _PRUnixPollDesc *unixpds, *unixpd; + _PRCPU *io_cpu; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_IS_NATIVE_THREAD(me)) { + fd_set rd, wt, ex; + struct timeval tv, *tvp = NULL; + int maxfd = -1; + /* + * For restarting _MD_SELECT() if it is interrupted by a signal. + * We use these variables to figure out how much time has elapsed + * and how much of the timeout still remains. + */ + PRIntervalTime start, elapsed, remaining; + + FD_ZERO(&rd); + FD_ZERO(&wt); + FD_ZERO(&ex); + + 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; + + if (osfd > maxfd) { + maxfd = 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; + start = PR_IntervalNow(); + } + +retry: + n = _MD_SELECT(maxfd + 1, &rd, &wt, &ex, tvp); + if (n == -1 && errno == EINTR) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } else { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + n = 0; /* timed out */ + } else { + remaining = timeout - elapsed; + tv.tv_sec = PR_IntervalToSeconds(remaining); + tv.tv_usec = PR_IntervalToMicroseconds( + remaining - PR_SecondsToInterval(tv.tv_sec)); + goto retry; + } + } + } + + if (n > 0) { + n = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRInt32 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 = 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 < 0) { + err = _MD_ERRNO(); + if (err == EBADF) { + /* Find the bad fds */ + n = 0; + for (pd = pds, epd = pd + npds; pd < epd; pd++) { + PRFileDesc *bottom = pd->fd; + pd->out_flags = 0; + if ((NULL == bottom) || (pd->in_flags == 0)) { + continue; + } + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + if (fcntl(bottom->secret->md.osfd, F_GETFL, 0) == -1) { + pd->out_flags = PR_POLL_NVAL; + n++; + } + } + PR_ASSERT(n > 0); + } else { + PR_ASSERT(err != EINTR); /* should have been handled above */ + _PR_MD_MAP_SELECT_ERROR(err); + } + } + + return n; + } + + /* + * XXX + * PRPollDesc has a PRFileDesc field, fd, while the IOQ + * is a list of PRPollQueue structures, each of which contains + * a _PRUnixPollDesc. A _PRUnixPollDesc struct contains + * the OS file descriptor, osfd, and not a PRFileDesc. + * So, we have allocate memory for _PRUnixPollDesc structures, + * copy the flags information from the pds list and have pq + * point to this list of _PRUnixPollDesc structures. + * + * It would be better if the memory allocation can be avoided. + */ + + unixpds = (_PRUnixPollDesc*) PR_MALLOC(npds * sizeof(_PRUnixPollDesc)); + if (!unixpds) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return -1; + } + unixpd = unixpds; + + _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_FAST_INTSON(is); + PR_DELETE(unixpds); + 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); + + unixpd->osfd = osfd; + unixpd->in_flags = pd->in_flags; + unixpd++; + 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 = unixpds; + pq.npds = pdcnt; + + pq.thr = me; + io_cpu = me->cpu; + 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; + + /* + * This thread should run on the same cpu on which it was blocked; when + * the IO request times out the fd sets and fd counts for the + * cpu are updated below. + */ + PR_ASSERT(me->cpu == io_cpu); + /* + * Copy the out_flags from the _PRUnixPollDesc structures to the + * user's PRPollDesc structures and free the allocated memory + */ + unixpd = unixpds; + 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 = unixpd->out_flags; + unixpd++; + } + PR_DELETE(unixpds); + + /* + ** If we timed out the pollq might still be on the ioq. Remove it + ** before continuing. + */ + if (pq.on_ioq) { + _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; + } +} + + + + +/************************************************************************/ + +/* +** 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(); + + 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; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *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 (fcntl(osfd, F_GETFL, 0) == -1) { + /* Found a bad descriptor, remove it from the fd_sets. */ + 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; + } + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } +} + +/************************************************************************/ + +/* +** Called by the scheduler when there is nothing to do. This means that +** all threads are blocked on some monitor somewhere. +** +** Note: this code doesn't release the scheduler lock. +*/ +/* +** Pause the current CPU. longjmp to the cpu's pause stack +** +** This must be called with the scheduler locked +*/ +void _MD_PauseCPU(PRIntervalTime ticks) +{ + PRThread *me = _MD_CURRENT_THREAD(); +#ifdef _PR_USE_POLL + int timeout; + struct pollfd *pollfds; /* an array of pollfd structures */ + struct pollfd *pollfdPtr; /* a pointer that steps through the array */ + unsigned long npollfds; /* number of pollfd structures in array */ + int nfd; /* to hold the return value of poll() */ +#else + struct timeval timeout, *tvp; + fd_set r, w, e; + fd_set *rp, *wp, *ep; + PRInt32 max_osfd, nfd; +#endif /* _PR_USE_POLL */ + PRInt32 rv; + PRCList *q; + PRUint32 min_timeout; + sigset_t oldset; +#ifdef IRIX +extern sigset_t ints_off; +#endif + + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + + _PR_MD_IOQ_LOCK(); + +#ifdef _PR_USE_POLL + /* Build up the pollfd structure array to wait on */ + + /* Find out how many pollfd structures are needed */ + npollfds = 0; + for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + + npollfds += pq->npds; + } + /* + * We use a pipe to wake up a native thread. An fd is needed + * for the pipe and we poll it for reading. + */ + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) + npollfds++; + + pollfds = (struct pollfd *) PR_MALLOC(npollfds * sizeof(struct pollfd)); + pollfdPtr = pollfds; + + /* + * If we need to poll the pipe for waking up a native thread, + * the pipe's fd is the first element in the pollfds array. + */ + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + pollfdPtr->fd = _pr_md_pipefd[0]; + pollfdPtr->events = PR_POLL_READ; + pollfdPtr++; + } + + min_timeout = PR_INTERVAL_NO_TIMEOUT; + for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + + if (pq->timeout < min_timeout) { + min_timeout = pq->timeout; + } + for (; pds < epds; pds++, pollfdPtr++) { + /* + * Assert that the pollfdPtr pointer does not go + * beyond the end of the pollfds array + */ + PR_ASSERT(pollfdPtr < pollfds + npollfds); + pollfdPtr->fd = pds->osfd; + /* direct copy of poll flags */ + pollfdPtr->events = pds->in_flags; + } + } +#else + /* + * 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); +#endif /* _PR_USE_POLL */ + /* + ** 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; + } + +#ifdef _PR_USE_POLL + if (min_timeout == PR_INTERVAL_NO_TIMEOUT) + timeout = -1; + else + timeout = PR_IntervalToMilliseconds(min_timeout); +#else + 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; + } +#endif /* _PR_USE_POLL */ + + _PR_MD_IOQ_UNLOCK(); + _MD_CHECK_FOR_EXIT(); + /* + * check for i/o operations + */ +#ifndef _PR_NO_CLOCK_TIMER + /* + * Disable the clock interrupts while we are in select, if clock interrupts + * are enabled. Otherwise, when the select/poll calls are interrupted, the + * timer value starts ticking from zero again when the system call is restarted. + */ +#ifdef IRIX + /* + * SIGCHLD signal is used on Irix to detect he termination of an + * sproc by SIGSEGV, SIGBUS or SIGABRT signals when + * _nspr_terminate_on_error is set. + */ + if ((!_nspr_noclock) || (_nspr_terminate_on_error)) +#else + if (!_nspr_noclock) +#endif /* IRIX */ +#ifdef IRIX + sigprocmask(SIG_BLOCK, &ints_off, &oldset); +#else + PR_ASSERT(sigismember(&timer_set, SIGALRM)); + sigprocmask(SIG_BLOCK, &timer_set, &oldset); +#endif /* IRIX */ +#endif /* !_PR_NO_CLOCK_TIMER */ + +#ifndef _PR_USE_POLL + PR_ASSERT(FD_ISSET(_pr_md_pipefd[0],rp)); + nfd = _MD_SELECT(max_osfd, rp, wp, ep, tvp); +#else + nfd = _MD_POLL(pollfds, npollfds, timeout); +#endif /* !_PR_USE_POLL */ + +#ifndef _PR_NO_CLOCK_TIMER +#ifdef IRIX + if ((!_nspr_noclock) || (_nspr_terminate_on_error)) +#else + if (!_nspr_noclock) +#endif /* IRIX */ + sigprocmask(SIG_SETMASK, &oldset, 0); +#endif /* !_PR_NO_CLOCK_TIMER */ + + _MD_CHECK_FOR_EXIT(); + _PR_MD_IOQ_LOCK(); + /* + ** Notify monitors that are associated with the selected descriptors. + */ +#ifdef _PR_USE_POLL + if (nfd > 0) { + pollfdPtr = pollfds; + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + /* + * Assert that the pipe is the first element in the + * pollfds array. + */ + PR_ASSERT(pollfds[0].fd == _pr_md_pipefd[0]); + if ((pollfds[0].revents & PR_POLL_READ) && (nfd == 1)) { + /* + * woken up by another thread; read all the data + * in the pipe to empty the pipe + */ + while ((rv = read(_pr_md_pipefd[0], _pr_md_pipebuf, + PIPE_BUF)) == PIPE_BUF){ + } + PR_ASSERT((rv > 0) || ((rv == -1) && (errno == EAGAIN))); + } + pollfdPtr++; + } + for (q = _PR_IOQ(me->cpu).next; q != &_PR_IOQ(me->cpu); q = q->next) { + PRPollQueue *pq = _PR_POLLQUEUE_PTR(q); + PRBool notify = PR_FALSE; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + + for (; pds < epds; pds++, pollfdPtr++) { + /* + * Assert that the pollfdPtr pointer does not go beyond + * the end of the pollfds array. + */ + PR_ASSERT(pollfdPtr < pollfds + npollfds); + /* + * Assert that the fd's in the pollfds array (stepped + * through by pollfdPtr) are in the same order as + * the fd's in _PR_IOQ() (stepped through by q and pds). + * This is how the pollfds array was created earlier. + */ + PR_ASSERT(pollfdPtr->fd == pds->osfd); + pds->out_flags = pollfdPtr->revents; + /* Negative fd's are ignored by poll() */ + if (pds->osfd >= 0 && pds->out_flags) { + notify = PR_TRUE; + } + } + if (notify) { + PRIntn pri; + PRThread *thred; + + PR_REMOVE_LINK(&pq->links); + pq->on_ioq = PR_FALSE; + + /* + * Because this thread can run on a different cpu right + * after being added to the run queue, do not dereference + * pq + */ + thred = pq->thr; + _PR_THREAD_LOCK(thred); + 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); + if (_pr_md_idle_cpus > 1) + _PR_MD_WAKEUP_WAITER(thred); + } + _PR_THREAD_UNLOCK(thred); + } + } + } else if (nfd == -1) { + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("poll() failed with errno %d", errno)); + } + + /* done with pollfds */ + PR_DELETE(pollfds); +#else + 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; + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *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)); + } + } + + /* + * Because this thread can run on a different cpu right + * after being added to the run queue, do not dereference + * pq + */ + 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; + } + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if ((FD_ISSET(_pr_md_pipefd[0], rp)) && (nfd == 1)) { + /* + * woken up by another thread; read all the data + * in the pipe to empty the pipe + */ + while ((rv = + read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) + == PIPE_BUF){ + } + PR_ASSERT((rv > 0) || + ((rv == -1) && (errno == EAGAIN))); + } + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } + } else if (nfd < 0) { + if (errno == EBADF) { + FindBadFDs(); + } else { + PR_LOG(_pr_io_lm, PR_LOG_MAX, ("select() failed with errno %d", + errno)); + } + } else { + PR_ASSERT(nfd == 0); + /* + * compute the new value of _PR_IOQ_TIMEOUT + */ + 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); + _PRUnixPollDesc *pds = pq->pds; + _PRUnixPollDesc *epds = pds + pq->npds; + PRInt32 pq_max_osfd = -1; + + q = q->next; + for (; pds < epds; pds++) { + if (pds->osfd > pq_max_osfd) { + pq_max_osfd = pds->osfd; + } + } + 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; + } + if (_PR_IS_NATIVE_THREAD_SUPPORTED()) { + if (_PR_IOQ_MAX_OSFD(me->cpu) < _pr_md_pipefd[0]) + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + } + } +#endif /* _PR_USE_POLL */ + _PR_MD_IOQ_UNLOCK(); +} + +void _MD_Wakeup_CPUs() +{ + PRInt32 rv, data; + + data = 0; + rv = write(_pr_md_pipefd[1], &data, 1); + + while ((rv < 0) && (errno == EAGAIN)) { + /* + * pipe full, read all data in pipe to empty it + */ + while ((rv = + read(_pr_md_pipefd[0], _pr_md_pipebuf, PIPE_BUF)) + == PIPE_BUF) { + } + PR_ASSERT((rv > 0) || + ((rv == -1) && (errno == EAGAIN))); + rv = write(_pr_md_pipefd[1], &data, 1); + } +} + + +void _MD_InitCPUS() +{ + PRInt32 rv, flags; + PRThread *me = _MD_CURRENT_THREAD(); + + rv = pipe(_pr_md_pipefd); + PR_ASSERT(rv == 0); + _PR_IOQ_MAX_OSFD(me->cpu) = _pr_md_pipefd[0]; + FD_SET(_pr_md_pipefd[0], &_PR_FD_READ_SET(me->cpu)); + + flags = fcntl(_pr_md_pipefd[0], F_GETFL, 0); + fcntl(_pr_md_pipefd[0], F_SETFL, flags | O_NONBLOCK); + flags = fcntl(_pr_md_pipefd[1], F_GETFL, 0); + fcntl(_pr_md_pipefd[1], F_SETFL, flags | O_NONBLOCK); +} + +/* +** Unix SIGALRM (clock) signal handler +*/ +static void ClockInterruptHandler() +{ + int olderrno; + PRUintn pri; + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + PRThread *me = _MD_CURRENT_THREAD(); + +#ifdef SOLARIS + if (!me || _PR_IS_NATIVE_THREAD(me)) { + _pr_primordialCPU->u.missed[_pr_primordialCPU->where] |= _PR_MISSED_CLOCK; + return; + } +#endif + + if (_PR_MD_GET_INTSOFF() != 0) { + cpu->u.missed[cpu->where] |= _PR_MISSED_CLOCK; + return; + } + _PR_MD_SET_INTSOFF(1); + + olderrno = errno; + _PR_ClockInterrupt(); + errno = olderrno; + + /* + ** If the interrupt wants a resched or if some other thread at + ** the same priority needs the cpu, reschedule. + */ + pri = me->priority; + if ((cpu->u.missed[3] || (_PR_RUNQREADYMASK(me->cpu) >> pri))) { +#ifdef _PR_NO_PREEMPT + cpu->resched = PR_TRUE; + if (pr_interruptSwitchHook) { + (*pr_interruptSwitchHook)(pr_interruptSwitchHookArg); + } +#else /* _PR_NO_PREEMPT */ + /* + ** Re-enable unix interrupts (so that we can use + ** setjmp/longjmp for context switching without having to + ** worry about the signal state) + */ + sigprocmask(SIG_SETMASK, &empty_set, 0); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock caused context switch")); + + if(!(me->flags & _PR_IDLE_THREAD)) { + _PR_THREAD_LOCK(me); + me->state = _PR_RUNNABLE; + me->cpu = cpu; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(me); + } else + me->state = _PR_RUNNABLE; + _MD_SWITCH_CONTEXT(me); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("clock back from context switch")); +#endif /* _PR_NO_PREEMPT */ + } + /* + * Because this thread could be running on a different cpu after + * a context switch the current cpu should be accessed and the + * value of the 'cpu' variable should not be used. + */ + _PR_MD_SET_INTSOFF(0); +} + +/* # of milliseconds per clock tick that we will use */ +#define MSEC_PER_TICK 50 + + +void _MD_StartInterrupts() +{ + struct itimerval itval; + char *eval; + struct sigaction vtact; + + vtact.sa_handler = (void (*)()) ClockInterruptHandler; + vtact.sa_flags = SA_RESTART; + vtact.sa_mask = timer_set; + sigaction(SIGALRM, &vtact, 0); + + if ((eval = getenv("NSPR_NOCLOCK")) != NULL) { + if (atoi(eval) == 0) + _nspr_noclock = 0; + else + _nspr_noclock = 1; + } + +#ifndef _PR_NO_CLOCK_TIMER + if (!_nspr_noclock) { + itval.it_interval.tv_sec = 0; + itval.it_interval.tv_usec = MSEC_PER_TICK * PR_USEC_PER_MSEC; + itval.it_value = itval.it_interval; + setitimer(ITIMER_REAL, &itval, 0); + } +#endif +} + +void _MD_StopInterrupts() +{ + sigprocmask(SIG_BLOCK, &timer_set, 0); +} + +void _MD_DisableClockInterrupts() +{ + struct itimerval itval; + extern PRUintn _pr_numCPU; + + PR_ASSERT(_pr_numCPU == 1); + if (!_nspr_noclock) { + itval.it_interval.tv_sec = 0; + itval.it_interval.tv_usec = 0; + itval.it_value = itval.it_interval; + setitimer(ITIMER_REAL, &itval, 0); + } +} + +void _MD_BlockClockInterrupts() +{ + sigprocmask(SIG_BLOCK, &timer_set, 0); +} + +void _MD_UnblockClockInterrupts() +{ + sigprocmask(SIG_UNBLOCK, &timer_set, 0); +} + +void _MD_MakeNonblock(PRFileDesc *fd) +{ + PRInt32 osfd = fd->secret->md.osfd; + int flags; + + if (osfd <= 2) { + /* Don't mess around with stdin, stdout or stderr */ + return; + } + flags = fcntl(osfd, F_GETFL, 0); + + /* + * Use O_NONBLOCK (POSIX-style non-blocking I/O) whenever possible. + * On SunOS 4, we must use FNDELAY (BSD-style non-blocking I/O), + * otherwise connect() still blocks and can be interrupted by SIGALRM. + */ + +#ifdef SUNOS4 + fcntl(osfd, F_SETFL, flags | FNDELAY); +#else + fcntl(osfd, F_SETFL, flags | O_NONBLOCK); +#endif + } + +PRInt32 _MD_open(const char *name, PRIntn flags, PRIntn mode) +{ + PRInt32 osflags; + PRInt32 rv, err; + + if (flags & PR_RDWR) { + osflags = O_RDWR; + } else if (flags & PR_WRONLY) { + osflags = O_WRONLY; + } else { + osflags = O_RDONLY; + } + + if (flags & PR_APPEND) + osflags |= O_APPEND; + if (flags & PR_TRUNCATE) + osflags |= O_TRUNC; + if (flags & PR_SYNC) { +#if defined(FREEBSD) + osflags |= O_FSYNC; +#else + osflags |= O_SYNC; +#endif + } + + /* + ** On creations we hold the 'create' lock in order to enforce + ** the semantics of PR_Rename. (see the latter for more details) + */ + if (flags & PR_CREATE_FILE) + { + osflags |= O_CREAT ; + if (NULL !=_pr_rename_lock) + PR_Lock(_pr_rename_lock); + } + + rv = open(name, osflags, mode); + + if (rv < 0) { + err = _MD_ERRNO(); + _PR_MD_MAP_OPEN_ERROR(err); + } + + if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock)) + PR_Unlock(_pr_rename_lock); + return rv; +} + +PRIntervalTime intr_timeout_ticks; + +static void sigsegvhandler() { + fprintf(stderr,"Received SIGSEGV\n"); + fflush(stderr); + pause(); +} + +static void sigaborthandler() { + fprintf(stderr,"Received SIGABRT\n"); + fflush(stderr); + pause(); +} + +static void sigbushandler() { + fprintf(stderr,"Received SIGBUS\n"); + fflush(stderr); + pause(); +} + +#endif /* !defined(_PR_PTHREADS) */ + +void _PR_UnixInit() +{ + struct sigaction sigact; + int rv; + + sigemptyset(&timer_set); + +#if !defined(_PR_PTHREADS) + + sigaddset(&timer_set, SIGALRM); + sigemptyset(&empty_set); + intr_timeout_ticks = + PR_SecondsToInterval(_PR_INTERRUPT_CHECK_INTERVAL_SECS); + +#if defined(SOLARIS) || defined(IRIX) + + if (getenv("NSPR_SIGSEGV_HANDLE")) { + sigact.sa_handler = sigsegvhandler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = timer_set; + sigaction(SIGSEGV, &sigact, 0); + } + + if (getenv("NSPR_SIGABRT_HANDLE")) { + sigact.sa_handler = sigaborthandler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = timer_set; + sigaction(SIGABRT, &sigact, 0); + } + + if (getenv("NSPR_SIGBUS_HANDLE")) { + sigact.sa_handler = sigbushandler; + sigact.sa_flags = SA_RESTART; + sigact.sa_mask = timer_set; + sigaction(SIGBUS, &sigact, 0); + } + +#endif +#endif /* !defined(_PR_PTHREADS) */ + + /* + * Under HP-UX DCE threads, sigaction() installs a per-thread + * handler, so we use sigvector() to install a process-wide + * handler. + */ +#if defined(HPUX) && defined(_PR_DCETHREADS) + { + struct sigvec vec; + + vec.sv_handler = SIG_IGN; + vec.sv_mask = 0; + vec.sv_flags = 0; + rv = sigvector(SIGPIPE, &vec, NULL); + PR_ASSERT(0 == rv); + } +#else + sigact.sa_handler = SIG_IGN; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + rv = sigaction(SIGPIPE, &sigact, 0); + PR_ASSERT(0 == rv); +#endif /* HPUX && _PR_DCETHREADS */ + + _pr_rename_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_rename_lock); + _pr_Xfe_mon = PR_NewMonitor(); + PR_ASSERT(NULL != _pr_Xfe_mon); + +} + +/* + * _MD_InitSegs -- + * + * This is Unix's version of _PR_MD_INIT_SEGS(), which is + * called by _PR_InitSegs(), which in turn is called by + * PR_Init(). + */ +void _MD_InitSegs() +{ +#ifdef DEBUG + /* + ** Disable using mmap(2) if NSPR_NO_MMAP is set + */ + if (getenv("NSPR_NO_MMAP")) { + _pr_zero_fd = -2; + return; + } +#endif + _pr_zero_fd = open("/dev/zero",O_RDWR , 0); + _pr_md_lock = PR_NewLock(); +} + +PRStatus _MD_AllocSegment(PRSegment *seg, PRUint32 size, void *vaddr) +{ + static char *lastaddr = (char*) _PR_STACK_VMBASE; + PRStatus retval = PR_SUCCESS; + int prot; + void *rv; + + PR_ASSERT(seg != 0); + PR_ASSERT(size != 0); + + PR_Lock(_pr_md_lock); + if (_pr_zero_fd < 0) { +from_heap: + seg->vaddr = PR_MALLOC(size); + if (!seg->vaddr) { + retval = PR_FAILURE; + } + else { + seg->size = size; + seg->access = PR_SEGMENT_RDWR; + } + goto exit; + } + + prot = PROT_READ|PROT_WRITE; + rv = mmap((vaddr != 0) ? vaddr : lastaddr, size, prot, + _MD_MMAP_FLAGS, + _pr_zero_fd, 0); + if (rv == (void*)-1) { + goto from_heap; + } + lastaddr += size; + seg->vaddr = rv; + seg->size = size; + seg->access = PR_SEGMENT_RDWR; + seg->flags = _PR_SEG_VM; + +exit: + PR_Unlock(_pr_md_lock); + return retval; +} + +void _MD_FreeSegment(PRSegment *seg) +{ + if (seg->flags & _PR_SEG_VM) + (void) munmap(seg->vaddr, seg->size); + else + PR_DELETE(seg->vaddr); +} + +/* + *----------------------------------------------------------------------- + * + * 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 Unix + * implementation. + * Cf. time_t time(time_t *tp) + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRTime) +PR_Now(void) +{ + struct timeval tv; + PRInt64 s, us, s2us; + +#if (defined(SOLARIS) && defined(_SVID_GETTOD)) || defined(SONY) + gettimeofday(&tv); +#else + gettimeofday(&tv, 0); +#endif + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_I2L(s, tv.tv_sec); + LL_I2L(us, tv.tv_usec); + LL_MUL(s, s, s2us); + LL_ADD(s, s, us); + return s; +} + +PR_IMPLEMENT(PRIntervalTime) _PR_UNIX_GetInterval() +{ + struct timeval time; + PRIntervalTime ticks; + +#if defined(_SVID_GETTOD) || defined(SONY) + (void)gettimeofday(&time); /* fallicy of course */ +#else + (void)gettimeofday(&time, NULL); /* fallicy of course */ +#endif + ticks = (PRUint32)time.tv_sec * PR_MSEC_PER_SEC; /* that's in milliseconds */ + ticks += (PRUint32)time.tv_usec / PR_USEC_PER_MSEC; /* so's that */ + return ticks; +} /* _PR_SUNOS_GetInterval */ + +PR_IMPLEMENT(PRIntervalTime) _PR_UNIX_TicksPerSecond() +{ + return 1000; /* this needs some work :) */ +} + +/* + * _PR_UnixTransmitFile + * + * Send file fd across socket sd. If headers is non-NULL, 'hlen' + * bytes of headers is sent before sending the file. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + */ +#define TRANSMITFILE_MMAP_CHUNK (256 * 1024) +PR_IMPLEMENT(PRInt32) _PR_UnixTransmitFile(PRFileDesc *sd, PRFileDesc *fd, +const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, +PRIntervalTime timeout) +{ + PRInt32 rv, count = 0; + PRInt32 len, index = 0; + struct stat statbuf; + struct PRIOVec iov[2]; + void *addr; + PRInt32 err; + + /* Get file size */ + if (fstat(fd->secret->md.osfd, &statbuf) == -1) { + err = _MD_ERRNO(); + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ETIMEDOUT: +#ifdef ENOLINK + case ENOLINK: +#endif + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } + count = -1; + goto done; + } + /* + * If the file is large, mmap and send the file in chunks so as + * to not consume too much virtual address space + */ + len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? statbuf.st_size : + TRANSMITFILE_MMAP_CHUNK; + /* + * Map in (part of) file. Take care of zero-length files. + */ + if (len) { + addr = mmap((caddr_t) 0, len, PROT_READ, MAP_PRIVATE, + fd->secret->md.osfd, 0); + + if (addr == (void*)-1) { + _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); + count = -1; + goto done; + } + } + /* + * send headers, first, followed by the file + */ + if (hlen) { + iov[index].iov_base = (char *) headers; + iov[index].iov_len = hlen; + index++; + } + iov[index].iov_base = (char*)addr; + iov[index].iov_len = len; + index++; + rv = PR_Writev(sd, iov, index, timeout); + if (len) + munmap(addr,len); + if (rv >= 0) { + PR_ASSERT(rv == hlen + len); + statbuf.st_size -= len; + count += rv; + } else { + count = -1; + goto done; + } + /* + * send remaining bytes of the file, if any + */ + len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? statbuf.st_size : + TRANSMITFILE_MMAP_CHUNK; + while (len > 0) { + /* + * Map in (part of) file + */ + PR_ASSERT((count - hlen) % TRANSMITFILE_MMAP_CHUNK == 0); + addr = mmap((caddr_t) 0, len, PROT_READ, MAP_PRIVATE, + fd->secret->md.osfd, count - hlen); + + if (addr == (void*)-1) { + _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); + count = -1; + goto done; + } + rv = PR_Send(sd, addr, len, 0, timeout); + munmap(addr,len); + if (rv >= 0) { + PR_ASSERT(rv == len); + statbuf.st_size -= rv; + count += rv; + len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? + statbuf.st_size : TRANSMITFILE_MMAP_CHUNK; + } else { + count = -1; + goto done; + } + } +done: + if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) + PR_Close(sd); + return count; +} + +#if defined(HPUX11) && !defined(_PR_PTHREADS) + +/* + * _PR_HPUXTransmitFile + * + * Send file fd across socket sd. If headers is non-NULL, 'hlen' + * bytes of headers is sent before sending the file. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in HP-UX B.11.00. + * + * Known problem: sendfile() does not work with NSPR's malloc() + * functions. The reason is unknown. So if you want to use + * _PR_HPUXTransmitFile(), you must not override the native malloc() + * functions. + */ + +PRInt32 +_PR_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, + const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, + PRIntervalTime timeout) +{ + struct stat statbuf; + PRInt32 nbytes_to_send; + off_t offset; + struct iovec hdtrl[2]; /* optional header and trailer buffers */ + int send_flags; + PRInt32 count; + PRInt32 rv, err; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + /* Get file size */ + if (fstat(fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + nbytes_to_send = hlen + statbuf.st_size; + offset = 0; + + hdtrl[0].iov_base = (void *) headers; /* cast away the 'const' */ + hdtrl[0].iov_len = hlen; + hdtrl[1].iov_base = NULL; + hdtrl[1].iov_base = 0; + /* + * SF_DISCONNECT seems to disconnect the socket even if sendfile() + * only does a partial send on a nonblocking socket. This + * would prevent the subsequent sendfile() calls on that socket + * from working. So we don't use the SD_DISCONNECT flag. + */ + send_flags = 0; + rv = 0; + + while (1) { + count = sendfile(sd->secret->md.osfd, fd->secret->md.osfd, + offset, 0, hdtrl, send_flags); + PR_ASSERT(count <= nbytes_to_send); + if (count == -1) { + err = errno; + if (err == EINTR) { + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + continue; /* retry */ + } + if (err != EAGAIN && err != EWOULDBLOCK) { + _MD_hpux_map_sendfile_error(err); + return -1; + } + count = 0; + } + rv += count; + + if (count < nbytes_to_send) { + /* + * Optimization: if bytes sent is less than requested, call + * select before returning. This is because it is likely that + * the next sendfile() call will return EWOULDBLOCK. + */ + if (!_PR_IS_NATIVE_THREAD(me)) { + if (_PR_WaitForFD(sd->secret->md.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); + } + return -1; + } else if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError( PR_PENDING_INTERRUPT_ERROR, 0); + return -1; + } + } else { + if (socket_io_wait(sd->secret->md.osfd, WRITE_FD, timeout)< 0) { + return -1; + } + } + + if (hdtrl[0].iov_len == 0) { + PR_ASSERT(hdtrl[0].iov_base == NULL); + offset += count; + } else if (count < hdtrl[0].iov_len) { + PR_ASSERT(offset == 0); + hdtrl[0].iov_base = (char *) hdtrl[0].iov_base + count; + hdtrl[0].iov_len -= count; + } else { + offset = count - hdtrl[0].iov_len; + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + } + nbytes_to_send -= count; + } else { + break; /* done */ + } + } + + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + return rv; +} + +#endif /* HPUX11 && !_PR_PTHREADS */ + +#if !defined(_PR_PTHREADS) +/* +** Wait for I/O on a single descriptor. + * + * return 0, if timed-out or interrupted, else return 1 +*/ +PRInt32 _PR_WaitForFD(PRInt32 osfd, PRUintn how, PRIntervalTime timeout) +{ + _PRUnixPollDesc pd; + PRPollQueue pq; + PRIntn is; + PRInt32 rv = 1; + _PRCPU *io_cpu; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); + PR_LOG(_pr_io_lm, PR_LOG_MIN, + ("waiting to %s on osfd=%d", + (how == PR_POLL_READ) ? "read" : "write", + osfd)); + + 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(); + _PR_FAST_INTSON(is); + return 0; + } + + pq.thr = me; + io_cpu = me->cpu; + 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_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; + + /* + * This thread should run on the same cpu on which it was blocked; when + * the IO request times out the fd sets and fd counts for the + * cpu are updated below. + */ + PR_ASSERT(me->cpu == io_cpu); + + /* + ** If we timed out the pollq might still be on the ioq. Remove it + ** before continuing. + */ + if (pq.on_ioq) { + _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; + + /* + * GLOBAL threads wakeup periodically to check for interrupt + */ + if (_PR_IS_NATIVE_THREAD(thr)) { + _PR_THREAD_UNLOCK(thr); + return; + } + + 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); +} +#endif /* !defined(_PR_PTHREADS) */ + +/* + * When a nonblocking connect has completed, determine whether it + * succeeded or failed, and if it failed, what the error code is. + * + * The function returns the error code. An error code of 0 means + * that the nonblocking connect succeeded. + */ + +int _MD_unix_get_nonblocking_connect_error(int osfd) +{ +#if defined(NCR) || defined(UNIXWARE) || defined(SNI) || defined(NEC) + /* + * getsockopt() fails with EPIPE, so use getmsg() instead. + */ + + int rv; + int flags = 0; + rv = getmsg(osfd, NULL, NULL, &flags); + PR_ASSERT(-1 == rv || 0 == rv); + if (-1 == rv && errno != EAGAIN && errno != EWOULDBLOCK) { + return errno; + } + return 0; /* no error */ +#else + int err; + _PRSockLen_t optlen = sizeof(err); + if (getsockopt(osfd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) == -1) { + return errno; + } else { + return err; + } +#endif +} + +/************************************************************************/ + +/* +** Special hacks for xlib. Xlib/Xt/Xm is not re-entrant nor is it thread +** safe. Unfortunately, neither is mozilla. To make these programs work +** in a pre-emptive threaded environment, we need to use a lock. +*/ + +void PR_XLock() +{ + PR_EnterMonitor(_pr_Xfe_mon); +} + +void PR_XUnlock() +{ + PR_ExitMonitor(_pr_Xfe_mon); +} + +PRBool PR_XIsLocked() +{ + return (PR_InMonitor(_pr_Xfe_mon)) ? PR_TRUE : PR_FALSE; +} + +void PR_XWait(int ms) +{ + PR_Wait(_pr_Xfe_mon, PR_MillisecondsToInterval(ms)); +} + +void PR_XNotify(void) +{ + PR_Notify(_pr_Xfe_mon); +} + +void PR_XNotifyAll(void) +{ + PR_NotifyAll(_pr_Xfe_mon); +} + +#ifdef HAVE_BSD_FLOCK + +#include <sys/file.h> + +PR_IMPLEMENT(PRStatus) +_MD_LockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_EX); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_TLockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_EX|LOCK_NB); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_UnlockFile(PRInt32 f) +{ + PRInt32 rv; + rv = flock(f, LOCK_UN); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_FLOCK_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} +#else + +PR_IMPLEMENT(PRStatus) +_MD_LockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_LOCK, 0); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_TLockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_TLOCK, 0); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +PR_IMPLEMENT(PRStatus) +_MD_UnlockFile(PRInt32 f) +{ + PRInt32 rv; + rv = lockf(f, F_ULOCK, 0); + if (rv == 0) + return PR_SUCCESS; + _PR_MD_MAP_LOCKF_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} +#endif + +PR_IMPLEMENT(PRStatus) _MD_gethostname(char *name, PRUint32 namelen) +{ + PRIntn rv; + + rv = gethostname(name, namelen); + if (0 == rv) { + return PR_SUCCESS; + } + _PR_MD_MAP_GETHOSTNAME_ERROR(_MD_ERRNO()); + return PR_FAILURE; +} + +/* + ******************************************************************* + * + * Memory-mapped files + * + ******************************************************************* + */ + +PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) +{ + PRFileInfo info; + PRUint32 sz; + + LL_L2UI(sz, size); + if (sz) { + if (PR_GetOpenFileInfo(fmap->fd, &info) == PR_FAILURE) { + return PR_FAILURE; + } + if (sz > info.size) { + /* + * Need to extend the file + */ + if (fmap->prot != PR_PROT_READWRITE) { + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, 0); + return PR_FAILURE; + } + if (PR_Seek(fmap->fd, sz - 1, PR_SEEK_SET) == -1) { + return PR_FAILURE; + } + if (PR_Write(fmap->fd, "", 1) != 1) { + return PR_FAILURE; + } + } + } + if (fmap->prot == PR_PROT_READONLY) { + fmap->md.prot = PROT_READ; + fmap->md.flags = 0; + } else if (fmap->prot == PR_PROT_READWRITE) { + fmap->md.prot = PROT_READ | PROT_WRITE; + fmap->md.flags = MAP_SHARED; + } else { + PR_ASSERT(fmap->prot == PR_PROT_WRITECOPY); + fmap->md.prot = PROT_READ | PROT_WRITE; + fmap->md.flags = MAP_PRIVATE; + } + return PR_SUCCESS; +} + +void * _MD_MemMap( + PRFileMap *fmap, + PRInt64 offset, + PRUint32 len) +{ + PRInt32 off; + void *addr; + + LL_L2I(off, offset); + if ((addr = mmap(0, len, fmap->md.prot, fmap->md.flags, + fmap->fd->secret->md.osfd, off)) == (void *) -1) { + _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); + addr = NULL; + } + return addr; +} + +PRStatus _MD_MemUnmap(void *addr, PRUint32 len) +{ + if (munmap(addr, len) == 0) { + return PR_SUCCESS; + } else { + if (errno == EINVAL) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, errno); + } else { + PR_SetError(PR_UNKNOWN_ERROR, errno); + } + return PR_FAILURE; + } +} + +PRStatus _MD_CloseFileMap(PRFileMap *fmap) +{ + PR_DELETE(fmap); + return PR_SUCCESS; +} diff --git a/pr/src/md/unix/unix_errors.c b/pr/src/md/unix/unix_errors.c new file mode 100644 index 00000000..92d96c73 --- /dev/null +++ b/pr/src/md/unix/unix_errors.c @@ -0,0 +1,1484 @@ +/* -*- 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 "prtypes.h" +#include "md/_unix_errors.h" +#include "prerror.h" +#include <errno.h> + +void _MD_unix_map_opendir_error(int err) +{ + switch (err) { + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case ENFILE: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_closedir_error(int err) +{ + switch (err) { + case EINVAL: + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_readdir_error(int err) +{ + + switch (err) { + case ENOENT: + PR_SetError(PR_NO_MORE_FILES_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; +#ifdef IRIX +#ifdef IRIX5_3 +#else + case EDIRCORRUPTED: + PR_SetError(PR_DIRECTORY_CORRUPTED_ERROR, err); + break; +#endif +#endif +#ifdef EOVERFLOW + case EOVERFLOW: + PR_SetError(PR_IO_ERROR, err); + break; +#endif + case EINVAL: + PR_SetError(PR_IO_ERROR, err); + break; +#ifdef EBADMSG + case EBADMSG: + PR_SetError(PR_IO_ERROR, err); + break; +#endif + case EDEADLK: + PR_SetError(PR_DEADLOCK_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ENOLCK: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; +#ifdef ENOLINK + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + case ENXIO: + PR_SetError(PR_IO_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_unlink_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EBUSY: + PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EPERM: + PR_SetError(PR_IS_DIRECTORY_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_stat_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ETIMEDOUT: +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: +#endif + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; +#ifdef EOVERFLOW + case EOVERFLOW: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_fstat_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ETIMEDOUT: +#ifdef ENOLINK + case ENOLINK: +#endif + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#ifdef EOVERFLOW + case EOVERFLOW: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_rename_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EBUSY: + PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err); + break; +#ifdef EDQUOT + case EDQUOT: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; +#endif + case EEXIST: + PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EISDIR: + PR_SetError(PR_IS_DIRECTORY_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; + case EXDEV: + PR_SetError(PR_NOT_SAME_DEVICE_ERROR, err); + break; + case EMLINK: + PR_SetError(PR_MAX_DIRECTORY_ENTRIES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_access_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: +#endif + case ETIMEDOUT: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_mkdir_error(int err) +{ + switch (err) { + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EEXIST: + PR_SetError(PR_FILE_EXISTS_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; + case EMLINK: + PR_SetError(PR_MAX_DIRECTORY_ENTRIES_ERROR, err); + break; + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; +#ifdef EDQUOT + case EDQUOT: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; +#endif + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_rmdir_error(int err) +{ + + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EBUSY: + PR_SetError(PR_FILESYSTEM_MOUNTED_ERROR, err); + break; + case EEXIST: + PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_DIRECTORY_NOT_EMPTY_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: +#endif + case ETIMEDOUT: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_read_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; +#ifdef EBADMSG + case EBADMSG: + PR_SetError(PR_IO_ERROR, err); + break; +#endif + case EDEADLK: + PR_SetError(PR_DEADLOCK_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ENOLCK: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case ENXIO: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EISDIR: + PR_SetError(PR_IS_DIRECTORY_ERROR, err); + break; + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; +#ifdef ENOLINK + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_write_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EDEADLK: + PR_SetError(PR_DEADLOCK_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EFBIG: + PR_SetError(PR_FILE_TOO_BIG_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case ENOLCK: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; + case ENXIO: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + case ERANGE: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; +#ifdef EDQUOT + case EDQUOT: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; +#endif +#ifdef ENOLINK + case ENOLINK: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_lseek_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ESPIPE: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_fsync_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; +#ifdef ENOLINK + case ENOLINK: +#endif + case ETIMEDOUT: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_METHOD_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_close_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; +#ifdef ENOLINK + case ENOLINK: +#endif + case ETIMEDOUT: + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_socket_error(int err) +{ + switch (err) { + case EPROTONOSUPPORT: + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, err); + break; + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case ENFILE: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; +#if !defined(SCO_SV) + case ENOBUFS: +#endif /* !defined(SCO_SV) */ + case ENOMEM: +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_socketavailable_error(int err) +{ + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); +} + +void _MD_unix_map_recv_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_recvfrom_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case ECONNRESET: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_send_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; +#if !defined(SCO_SV) + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif /* !defined(SCO_SV) */ + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_sendto_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EMSGSIZE: + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; +#if !defined(SCO_SV) + case ENOBUFS: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif /* !defined(SCO_SV) */ + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_writev_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case ECONNRESET: + case EPIPE: + PR_SetError(PR_CONNECT_RESET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_accept_error(int err) +{ + switch (err) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + PR_SetError(PR_WOULD_BLOCK_ERROR, err); + break; + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EOPNOTSUPP: + case ENODEV: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case ENFILE: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif +#ifdef EPROTO + case EPROTO: + PR_SetError(PR_IO_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_connect_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EINPROGRESS: + PR_SetError(PR_IN_PROGRESS_ERROR, err); + break; + case EALREADY: + PR_SetError(PR_ALREADY_INITIATED_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EAFNOSUPPORT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case EISCONN: + PR_SetError(PR_IS_CONNECTED_ERROR, err); + break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, err); + break; + case ECONNREFUSED: + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); + break; + case ENETUNREACH: + PR_SetError(PR_NETWORK_UNREACHABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + /* + * UNIX domain sockets are not supported in NSPR + */ + case EACCES: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EIO: +#if defined(UNIXWARE) || defined(SNI) || defined(NEC) + /* + * On some platforms, if we connect to a port on + * the local host (the loopback address) that no + * process is listening on, we get EIO instead + * of ECONNREFUSED. + */ + PR_SetError(PR_CONNECT_REFUSED_ERROR, err); +#else + PR_SetError(PR_IO_ERROR, err); +#endif + break; + case ELOOP: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + case ENOENT: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + case ENXIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EPROTOTYPE: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_bind_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EADDRNOTAVAIL: + PR_SetError(PR_ADDRESS_NOT_AVAILABLE_ERROR, err); + break; + case EADDRINUSE: + PR_SetError(PR_ADDRESS_IN_USE_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_SOCKET_ADDRESS_IS_BOUND_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + /* + * UNIX domain sockets are not supported in NSPR + */ + case EIO: + case EISDIR: + case ELOOP: + case ENOENT: + case ENOTDIR: + case EROFS: + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_listen_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EOPNOTSUPP: + PR_SetError(PR_NOT_TCP_SOCKET_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_shutdown_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; +#endif + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_socketpair_error(int err) +{ + switch (err) { + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case EAFNOSUPPORT: + case EPROTONOSUPPORT: + case EOPNOTSUPP: + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_getsockname_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#if !defined(SCO_SV) + case ENOBUFS: +#endif /* !defined(SCO_SV) */ + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_getpeername_error(int err) +{ + + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOTCONN: + PR_SetError(PR_NOT_CONNECTED_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; +#if !defined(SCO_SV) + case ENOBUFS: +#endif /* !defined(SCO_SV) */ + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_getsockopt_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_setsockopt_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case ENOTSOCK: + PR_SetError(PR_NOT_SOCKET_ERROR, err); + break; + case ENOPROTOOPT: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, err); + break; + case ENOMEM: +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_open_error(int err) +{ + switch (err) { + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case EAGAIN: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case EBUSY: + PR_SetError(PR_IO_ERROR, err); + break; + case EEXIST: + PR_SetError(PR_FILE_EXISTS_ERROR, err); + break; + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + case EIO: + PR_SetError(PR_IO_ERROR, err); + break; + case EISDIR: + PR_SetError(PR_IS_DIRECTORY_ERROR, err); + break; + case ELOOP: + PR_SetError(PR_LOOP_ERROR, err); + break; + case EMFILE: + PR_SetError(PR_PROC_DESC_TABLE_FULL_ERROR, err); + break; + case ENAMETOOLONG: + PR_SetError(PR_NAME_TOO_LONG_ERROR, err); + break; + case ENFILE: + PR_SetError(PR_SYS_DESC_TABLE_FULL_ERROR, err); + break; + case ENODEV: + case ENOENT: + case ENXIO: + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ENOSPC: + PR_SetError(PR_NO_DEVICE_SPACE_ERROR, err); + break; +#ifdef ENOSR + case ENOSR: +#endif + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case ENOTDIR: + PR_SetError(PR_NOT_DIRECTORY_ERROR, err); + break; + case EPERM: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ETIMEDOUT: +#if !defined(OSF1) && !defined(FREEBSD) && !defined(BSDI) + case EMULTIHOP: + case ENOLINK: +#endif + PR_SetError(PR_REMOTE_FILE_ERROR, err); + break; + case EROFS: + PR_SetError(PR_READ_ONLY_FILESYSTEM_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_mmap_error(int err) +{ + + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EAGAIN: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + case EACCES: + PR_SetError(PR_NO_ACCESS_RIGHTS_ERROR, err); + break; + case ENOMEM: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_gethostname_error(int err) +{ + switch (err) { + case EFAULT: + PR_SetError(PR_ACCESS_FAULT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_select_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, err); + break; + case EINVAL: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_flock_error(int err) +{ + switch (err) { + case EBADF: + case EINVAL: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EWOULDBLOCK: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +void _MD_unix_map_lockf_error(int err) +{ + switch (err) { + case EBADF: + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, err); + break; + case EACCES: + PR_SetError(PR_FILE_IS_LOCKED_ERROR, err); + break; + case EDEADLK: + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, err); + break; + default: + PR_SetError(PR_UNKNOWN_ERROR, err); + break; + } +} + +#ifdef HPUX11 +void _MD_hpux_map_sendfile_error(int oserror) +{ + PRErrorCode prerror; + + switch (oserror) { + case ENOTSOCK: + prerror = PR_NOT_SOCKET_ERROR; + break; + case EFAULT: + prerror = PR_ACCESS_FAULT_ERROR; + break; + case ENOBUFS: + prerror = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case EINVAL: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + case ENOTCONN: + prerror = PR_NOT_CONNECTED_ERROR; + break; + case EPIPE: + prerror = PR_CONNECT_RESET_ERROR; + break; + case ENOMEM: + prerror = PR_OUT_OF_MEMORY_ERROR; + break; + case EOPNOTSUPP: + prerror = PR_NOT_TCP_SOCKET_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + } + PR_SetError(prerror, oserror); +} +#endif /* HPUX11 */ diff --git a/pr/src/md/unix/unixware.c b/pr/src/md/unix/unixware.c new file mode 100644 index 00000000..4bc410bf --- /dev/null +++ b/pr/src/md/unix/unixware.c @@ -0,0 +1,548 @@ +/* -*- 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" + +#if !defined (USE_SVR4_THREADS) + +/* + * using only NSPR threads here + */ + +#include <setjmp.h> + +void _MD_EarlyInit(void) +{ +} + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) setjmp(CONTEXT(t)); + } + *np = sizeof(CONTEXT(t)) / sizeof(PRWord); + return (PRWord *) CONTEXT(t); +} + +#ifdef ALARMS_BREAK_TCP /* I don't think they do */ + +PRInt32 _MD_connect(PRInt32 osfd, const PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _connect(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); +} + +PRInt32 _MD_accept(PRInt32 osfd, PRNetAddr *addr, PRInt32 addrlen, + PRIntervalTime timeout) +{ + PRInt32 rv; + + _MD_BLOCK_CLOCK_INTERRUPTS(); + rv = _accept(osfd,addr,addrlen); + _MD_UNBLOCK_CLOCK_INTERRUPTS(); + return(rv); +} +#endif + +/* + * These are also implemented in pratom.c using NSPR locks. Any reason + * this might be better or worse? If you like this better, define + * _PR_HAVE_ATOMIC_OPS in include/md/unixware.h + */ +#ifdef _PR_HAVE_ATOMIC_OPS +/* Atomic operations */ +#include <stdio.h> +static FILE *_uw_semf; + +void +_MD_INIT_ATOMIC(void) +{ + /* Sigh. Sure wish SYSV semaphores weren't such a pain to use */ + if ((_uw_semf = tmpfile()) == NULL) + PR_ASSERT(0); + + return; +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)++; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + flockfile(_uw_semf); + (*val)--; + unflockfile(_uw_semf); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + flockfile(_uw_semf); + *val = newval; + unflockfile(_uw_semf); +} +#endif + +void +_MD_SET_PRIORITY(_MDThread *thread, PRUintn newPri) +{ + return; +} + +PRStatus +_MD_InitializeThread(PRThread *thread) +{ + return PR_SUCCESS; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + _PR_MD_SWITCH_CONTEXT(thread); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread) { + PR_ASSERT(!(thread->flags & _PR_GLOBAL_SCOPE)); + } + return PR_SUCCESS; +} + +/* These functions should not be called for Unixware */ +void +_MD_YIELD(void) +{ + PR_NOT_REACHED("_MD_YIELD should not be called for Unixware."); +} + +PRStatus +_MD_CREATE_THREAD( + PRThread *thread, + void (*start) (void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + PR_NOT_REACHED("_MD_CREATE_THREAD should not be called for Unixware."); +} + +#else /* USE_SVR4_THREADS */ + +/* NOTE: + * SPARC v9 (Ultras) do have an atomic test-and-set operation. But + * SPARC v8 doesn't. We should detect in the init if we are running on + * v8 or v9, and then use assembly where we can. + */ + +#include <thread.h> +#include <synch.h> + +static mutex_t _unixware_atomic = DEFAULTMUTEX; + +#define TEST_THEN_ADD(where, inc) \ + if (mutex_lock(&_unixware_atomic) != 0)\ + PR_ASSERT(0);\ + *where += inc;\ + if (mutex_unlock(&_unixware_atomic) != 0)\ + PR_ASSERT(0); + +#define TEST_THEN_SET(where, val) \ + if (mutex_lock(&_unixware_atomic) != 0)\ + PR_ASSERT(0);\ + *where = val;\ + if (mutex_unlock(&_unixware_atomic) != 0)\ + PR_ASSERT(0); + +void +_MD_INIT_ATOMIC(void) +{ +} + +void +_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + TEST_THEN_ADD(val, 1); +} + +void +_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + TEST_THEN_ADD(val, 0xffffffff); +} + +void +_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + TEST_THEN_SET(val, newval); +} + +#include <signal.h> +#include <errno.h> +#include <fcntl.h> + +#include <sys/lwp.h> +#include <sys/procfs.h> +#include <sys/syscall.h> + + +THREAD_KEY_T threadid_key; +THREAD_KEY_T cpuid_key; +THREAD_KEY_T last_thread_key; +static sigset_t set, oldset; + +void _MD_EarlyInit(void) +{ + THR_KEYCREATE(&threadid_key, NULL); + THR_KEYCREATE(&cpuid_key, NULL); + THR_KEYCREATE(&last_thread_key, NULL); + sigemptyset(&set); + sigaddset(&set, SIGALRM); +} + +PRStatus _MD_CREATE_THREAD(PRThread *thread, + void (*start)(void *), + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + long flags; + + /* mask out SIGALRM for native thread creation */ + thr_sigsetmask(SIG_BLOCK, &set, &oldset); + + flags = (state == PR_JOINABLE_THREAD ? THR_SUSPENDED/*|THR_NEW_LWP*/ + : THR_SUSPENDED|THR_DETACHED/*|THR_NEW_LWP*/); + if (thread->flags & _PR_GCABLE_THREAD) + flags |= THR_BOUND; + + if (thr_create(NULL, thread->stack->stackSize, + (void *(*)(void *)) start, (void *) thread, + flags, + &thread->md.handle)) { + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + return PR_FAILURE; + } + + + /* When the thread starts running, then the lwpid is set to the right + * value. Until then we want to mark this as 'uninit' so that + * its register state is initialized properly for GC */ + + thread->md.lwpid = -1; + thr_sigsetmask(SIG_SETMASK, &oldset, NULL); + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + + if (scope == PR_GLOBAL_THREAD) { + thread->flags |= _PR_GLOBAL_SCOPE; + } + + /* + ** Set the thread priority. This will also place the thread on + ** the runQ. + ** + ** Force PR_SetThreadPriority to set the priority by + ** setting thread->priority to 100. + */ + { + int pri; + pri = thread->priority; + thread->priority = 100; + PR_SetThreadPriority( thread, pri ); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("(0X%x)[Start]: on to runq at priority %d", + thread, thread->priority)); + } + + /* Activate the thread */ + if (thr_continue( thread->md.handle ) ) { + return PR_FAILURE; + } + return PR_SUCCESS; +} + +void _MD_cleanup_thread(PRThread *thread) +{ + thread_t hdl; + PRMonitor *mon; + + hdl = thread->md.handle; + + /* + ** First, suspend the thread (unless it's the active one) + ** Because we suspend it first, we don't have to use LOCK_SCHEDULER to + ** prevent both of us modifying the thread structure at the same time. + */ + if ( thread != _PR_MD_CURRENT_THREAD() ) { + thr_suspend(hdl); + } + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("(0X%x)[DestroyThread]\n", thread)); + + _MD_DESTROY_SEM(&thread->md.waiter_sem); +} + +void _MD_SET_PRIORITY(_MDThread *md_thread, PRUintn newPri) +{ + if(thr_setprio((thread_t)md_thread->handle, newPri)) { + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("_PR_SetThreadPriority: can't set thread priority\n")); + } +} + +void _MD_WAIT_CV( + struct _MDCVar *md_cv, struct _MDLock *md_lock, PRIntervalTime timeout) +{ + struct timespec tt; + PRUint32 msec; + int rv; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + msec = PR_IntervalToMilliseconds(timeout); + + GETTIME (&tt); + + tt.tv_sec += msec / PR_MSEC_PER_SEC; + tt.tv_nsec += (msec % PR_MSEC_PER_SEC) * PR_NSEC_PER_MSEC; + /* Check for nsec overflow - otherwise we'll get an EINVAL */ + if (tt.tv_nsec >= PR_NSEC_PER_SEC) { + tt.tv_sec++; + tt.tv_nsec -= PR_NSEC_PER_SEC; + } + me->md.sp = unixware_getsp(); + + + /* XXX Solaris 2.5.x gives back EINTR occasionally for no reason + * hence ignore EINTR for now */ + + COND_TIMEDWAIT(&md_cv->cv, &md_lock->lock, &tt); +} + +void _MD_lock(struct _MDLock *md_lock) +{ + mutex_lock(&md_lock->lock); +} + +void _MD_unlock(struct _MDLock *md_lock) +{ + mutex_unlock(&((md_lock)->lock)); +} + + +PRThread *_pr_current_thread_tls() +{ + PRThread *ret; + + thr_getspecific(threadid_key, (void **)&ret); + return ret; +} + +PRStatus +_MD_WAIT(PRThread *thread, PRIntervalTime ticks) +{ + _MD_WAIT_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +PRStatus +_MD_WAKEUP_WAITER(PRThread *thread) +{ + if (thread == NULL) { + return PR_SUCCESS; + } + _MD_POST_SEM(&thread->md.waiter_sem); + return PR_SUCCESS; +} + +_PRCPU *_pr_current_cpu_tls() +{ + _PRCPU *ret; + + thr_getspecific(cpuid_key, (void **)&ret); + return ret; +} + +PRThread *_pr_last_thread_tls() +{ + PRThread *ret; + + thr_getspecific(last_thread_key, (void **)&ret); + return ret; +} + +_MDLock _pr_ioq_lock; + +void _MD_INIT_IO (void) +{ + _MD_NEW_LOCK(&_pr_ioq_lock); +} + +PRStatus _MD_InitializeThread(PRThread *thread) +{ + if (!_PR_IS_NATIVE_THREAD(thread)) + return; + /* prime the sp; substract 4 so we don't hit the assert that + * curr sp > base_stack + */ + thread->md.sp = (uint_t) thread->stack->allocBase - sizeof(long); + thread->md.lwpid = _lwp_self(); + thread->md.handle = THR_SELF(); + + /* all threads on Solaris are global threads from NSPR's perspective + * since all of them are mapped to Solaris threads. + */ + thread->flags |= _PR_GLOBAL_SCOPE; + + /* For primordial/attached thread, we don't create an underlying native thread. + * So, _MD_CREATE_THREAD() does not get called. We need to do initialization + * like allocating thread's synchronization variables and set the underlying + * native thread's priority. + */ + if (thread->flags & (_PR_PRIMORDIAL | _PR_ATTACHED)) { + _MD_NEW_SEM(&thread->md.waiter_sem, 0); + _MD_SET_PRIORITY(&(thread->md), thread->priority); + } + return PR_SUCCESS; +} + +static sigset_t old_mask; /* store away original gc thread sigmask */ +static int gcprio; /* store away original gc thread priority */ +static lwpid_t *all_lwps=NULL; /* list of lwps that we suspended */ +static int num_lwps ; +static int suspendAllOn = 0; + +#define VALID_SP(sp, bottom, top) \ + (((uint_t)(sp)) > ((uint_t)(bottom)) && ((uint_t)(sp)) < ((uint_t)(top))) + +void unixware_preempt_off() +{ + sigset_t set; + (void)sigfillset(&set); + sigprocmask (SIG_SETMASK, &set, &old_mask); +} + +void unixware_preempt_on() +{ + sigprocmask (SIG_SETMASK, &old_mask, NULL); +} + +void _MD_Begin_SuspendAll() +{ + unixware_preempt_off(); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin_SuspendAll\n")); + /* run at highest prio so I cannot be preempted */ + thr_getprio(thr_self(), &gcprio); + thr_setprio(thr_self(), 0x7fffffff); + suspendAllOn = 1; +} + +void _MD_End_SuspendAll() +{ +} + +void _MD_End_ResumeAll() +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End_ResumeAll\n")); + thr_setprio(thr_self(), gcprio); + unixware_preempt_on(); + suspendAllOn = 0; +} + +void _MD_Suspend(PRThread *thr) +{ + int lwp_fd, result; + int lwp_main_proc_fd = 0; + + thr_suspend(thr->md.handle); + if (!(thr->flags & _PR_GCABLE_THREAD)) + return; + /* XXX Primordial thread can't be bound to an lwp, hence there is no + * way we can assume that we can get the lwp status for primordial + * thread reliably. Hence we skip this for primordial thread, hoping + * that the SP is saved during lock and cond. wait. + * XXX - Again this is concern only for java interpreter, not for the + * server, 'cause primordial thread in the server does not do java work + */ + if (thr->flags & _PR_PRIMORDIAL) + return; + + /* if the thread is not started yet then don't do anything */ + if (!suspendAllOn || thr->md.lwpid == -1) + return; + +} +void _MD_Resume(PRThread *thr) +{ + if (!(thr->flags & _PR_GCABLE_THREAD) || !suspendAllOn){ + /*XXX When the suspendAllOn is set, we will be trying to do lwp_suspend + * during that time we can't call any thread lib or libc calls. Hence + * make sure that no resume is requested for Non gcable thread + * during suspendAllOn */ + PR_ASSERT(!suspendAllOn); + thr_continue(thr->md.handle); + return; + } + if (thr->md.lwpid == -1) + return; + + if ( _lwp_continue(thr->md.lwpid) < 0) { + PR_ASSERT(0); /* ARGH, we are hosed! */ + } +} + + +PRWord *_MD_HomeGCRegisters(PRThread *t, int isCurrent, int *np) +{ + if (isCurrent) { + (void) getcontext(CONTEXT(t)); /* XXX tune me: set md_IRIX.c */ + } + *np = NGREG; + if (t->md.lwpid == -1) + memset(&t->md.context.uc_mcontext.gregs[0], 0, NGREG * sizeof(PRWord)); + return (PRWord*) &t->md.context.uc_mcontext.gregs[0]; +} + +int +_pr_unixware_clock_gettime (struct timespec *tp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + tp->tv_sec = tv.tv_sec; + tp->tv_nsec = tv.tv_usec * 1000; + return 0; +} + + +#endif /* USE_SVR4_THREADS */ diff --git a/pr/src/md/unix/uxproces.c b/pr/src/md/unix/uxproces.c new file mode 100644 index 00000000..7f8ab887 --- /dev/null +++ b/pr/src/md/unix/uxproces.c @@ -0,0 +1,715 @@ +/* -*- 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 <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> + +/* + ********************************************************************** + * + * The Unix process routines + * + ********************************************************************** + */ + +#define _PR_SIGNALED_EXITSTATUS 256 + +typedef enum pr_PidState { + _PR_PID_DETACHED, + _PR_PID_REAPED, + _PR_PID_WAITING +} pr_PidState; + +typedef struct pr_PidRecord { + pid_t pid; + int exitStatus; + pr_PidState state; + PRCondVar *reapedCV; + struct pr_PidRecord *next; +} pr_PidRecord; + +/* + * Irix sprocs and LinuxThreads are actually a kind of processes + * that can share the virtual address space and file descriptors. + */ +#if (defined(IRIX) && !defined(_PR_PTHREADS)) \ + || (defined(LINUX) && defined(_PR_PTHREADS)) +#define _PR_SHARE_CLONES +#endif + +/* + * The macro _PR_NATIVE_THREADS indicates that we are + * using native threads only, so waitpid() blocks just the + * calling thread, not the process. In this case, the waitpid + * daemon thread can safely block in waitpid(). So we don't + * need to catch SIGCHLD, and the pipe to unblock PR_Poll() is + * also not necessary. + */ + +#if defined(_PR_GLOBAL_THREADS_ONLY) \ + || (defined(_PR_PTHREADS) && !defined(LINUX)) +#define _PR_NATIVE_THREADS +#endif + +/* + * All the static variables used by the Unix process routines are + * collected in this structure. + */ + +static struct { + PRCallOnceType once; + PRThread *thread; + PRLock *ml; +#if defined(_PR_NATIVE_THREADS) + PRInt32 numProcs; + PRCondVar *cv; +#else + int pipefd[2]; +#endif + pr_PidRecord **pidTable; + +#ifdef _PR_SHARE_CLONES + struct pr_CreateProcOp *opHead, *opTail; +#endif +} pr_wp; + +#ifdef _PR_SHARE_CLONES +static int pr_waitpid_daemon_exit; + +void +_MD_unix_terminate_waitpid_daemon(void) +{ + if (pr_wp.thread) { + pr_waitpid_daemon_exit = 1; + write(pr_wp.pipefd[1], "", 1); + PR_JoinThread(pr_wp.thread); + } +} +#endif + +static PRStatus _MD_InitProcesses(void); +#if !defined(_PR_NATIVE_THREADS) +static void pr_InstallSigchldHandler(void); +#endif + +static PRProcess * +ForkAndExec( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + PRProcess *process; + + process = PR_NEW(PRProcess); + if (!process) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + process->md.pid = fork(); + if ((pid_t) -1 == process->md.pid) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, errno); + PR_DELETE(process); + return NULL; + } else if (0 == process->md.pid) { /* the child process */ + /* + * If the child process needs to exit, it must call _exit(). + * Do not call exit(), because exit() will flush and close + * the standard I/O file descriptors, and hence corrupt + * the parent process's standard I/O data structures. + */ + + if (attr) { + if (attr->stdinFd + && attr->stdinFd->secret->md.osfd != 0) { + if (dup2(attr->stdinFd->secret->md.osfd, 0) != 0) { + _exit(1); /* failed */ + } + close(attr->stdinFd->secret->md.osfd); + } + if (attr->stdoutFd + && attr->stdoutFd->secret->md.osfd != 1) { + if (dup2(attr->stdoutFd->secret->md.osfd, 1) != 1) { + _exit(1); /* failed */ + } + close(attr->stdoutFd->secret->md.osfd); + } + if (attr->stderrFd + && attr->stderrFd->secret->md.osfd != 2) { + if (dup2(attr->stderrFd->secret->md.osfd, 2) != 2) { + _exit(1); /* failed */ + } + close(attr->stderrFd->secret->md.osfd); + } + } + + (void)execve(path, argv, envp); + /* Whoops! It returned. That's a bad sign. */ + _exit(1); + } + +#if defined(_PR_NATIVE_THREADS) + PR_Lock(pr_wp.ml); + if (0 == pr_wp.numProcs++) { + PR_NotifyCondVar(pr_wp.cv); + } + PR_Unlock(pr_wp.ml); +#endif + return process; +} + +#ifdef _PR_SHARE_CLONES + +struct pr_CreateProcOp { + const char *path; + char *const *argv; + char *const *envp; + const PRProcessAttr *attr; + PRProcess *process; + PRErrorCode prerror; + PRInt32 oserror; + PRBool done; + PRCondVar *doneCV; + struct pr_CreateProcOp *next; +}; + +PRProcess * +_MD_CreateUnixProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + struct pr_CreateProcOp *op; + PRProcess *proc; + int rv; + + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { + return NULL; + } + + op = PR_NEW(struct pr_CreateProcOp); + if (NULL == op) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + op->path = path; + op->argv = argv; + op->envp = envp; + op->attr = attr; + op->done = PR_FALSE; + op->doneCV = PR_NewCondVar(pr_wp.ml); + if (NULL == op->doneCV) { + PR_DELETE(op); + return NULL; + } + PR_Lock(pr_wp.ml); + + /* add to the tail of op queue */ + op->next = NULL; + if (pr_wp.opTail) { + pr_wp.opTail->next = op; + pr_wp.opTail = op; + } else { + PR_ASSERT(NULL == pr_wp.opHead); + pr_wp.opHead = pr_wp.opTail = op; + } + + /* wake up the daemon thread */ + do { + rv = write(pr_wp.pipefd[1], "", 1); + } while (-1 == rv && EINTR == errno); + + while (op->done == PR_FALSE) { + PR_WaitCondVar(op->doneCV, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + PR_DestroyCondVar(op->doneCV); + proc = op->process; + if (!proc) { + PR_SetError(op->prerror, op->oserror); + } + PR_DELETE(op); + return proc; +} + +#else /* ! _PR_SHARE_CLONES */ + +PRProcess * +_MD_CreateUnixProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + if (PR_CallOnce(&pr_wp.once, _MD_InitProcesses) == PR_FAILURE) { + return NULL; + } + return ForkAndExec(path, argv, envp, attr); +} /* _MD_CreateUnixProcess */ + +#endif /* _PR_SHARE_CLONES */ + +/* + * The pid table is a hashtable. + * + * The number of buckets in the hashtable (NBUCKETS) must be a power of 2. + */ +#define NBUCKETS_LOG2 6 +#define NBUCKETS (1 << NBUCKETS_LOG2) +#define PID_HASH_MASK ((pid_t) (NBUCKETS - 1)) + +static pr_PidRecord * +FindPidTable(pid_t pid) +{ + pr_PidRecord *pRec; + int keyHash = (int) (pid & PID_HASH_MASK); + + pRec = pr_wp.pidTable[keyHash]; + while (pRec) { + if (pRec->pid == pid) { + break; + } + pRec = pRec->next; + } + return pRec; +} + +static void +InsertPidTable(pr_PidRecord *pRec) +{ + int keyHash = (int) (pRec->pid & PID_HASH_MASK); + + pRec->next = pr_wp.pidTable[keyHash]; + pr_wp.pidTable[keyHash] = pRec; +} + +static void +DeletePidTable(pr_PidRecord *pRec) +{ + int keyHash = (int) (pRec->pid & PID_HASH_MASK); + + if (pr_wp.pidTable[keyHash] == pRec) { + pr_wp.pidTable[keyHash] = pRec->next; + } else { + pr_PidRecord *pred, *cur; /* predecessor and current */ + + pred = pr_wp.pidTable[keyHash]; + cur = pred->next; + while (cur) { + if (cur == pRec) { + pred->next = cur->next; + break; + } + pred = cur; + cur = cur->next; + } + PR_ASSERT(cur != NULL); + } +} + +static int +ExtractExitStatus(int rawExitStatus) +{ + /* + * We did not specify the WCONTINUED and WUNTRACED options + * for waitpid, so these two events should not be reported. + */ + PR_ASSERT(!WIFSTOPPED(rawExitStatus)); +#ifdef WIFCONTINUED + PR_ASSERT(!WIFCONTINUED(rawExitStatus)); +#endif + if (WIFEXITED(rawExitStatus)) { + return WEXITSTATUS(rawExitStatus); + } else { + PR_ASSERT(WIFSIGNALED(rawExitStatus)); + return _PR_SIGNALED_EXITSTATUS; + } +} + +static void +ProcessReapedChildInternal(pid_t pid, int status) +{ + pr_PidRecord *pRec; + + pRec = FindPidTable(pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + pRec->pid = pid; + pRec->state = _PR_PID_REAPED; + pRec->exitStatus = ExtractExitStatus(status); + pRec->reapedCV = NULL; + InsertPidTable(pRec); + } else { + PR_ASSERT(pRec->state != _PR_PID_REAPED); + if (_PR_PID_DETACHED == pRec->state) { + PR_ASSERT(NULL == pRec->reapedCV); + DeletePidTable(pRec); + PR_DELETE(pRec); + } else { + PR_ASSERT(_PR_PID_WAITING == pRec->state); + PR_ASSERT(NULL != pRec->reapedCV); + pRec->exitStatus = ExtractExitStatus(status); + pRec->state = _PR_PID_REAPED; + PR_NotifyCondVar(pRec->reapedCV); + } + } +} + +#if defined(_PR_NATIVE_THREADS) + +/* + * If all the threads are native threads, the daemon thread is + * simpler. We don't need to catch the SIGCHLD signal. We can + * just have the daemon thread block in waitpid(). + */ + +static void WaitPidDaemonThread(void *unused) +{ + pid_t pid; + int status; + + while (1) { + PR_Lock(pr_wp.ml); + while (0 == pr_wp.numProcs) { + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + + while (1) { + do { + pid = waitpid((pid_t) -1, &status, 0); + } while ((pid_t) -1 == pid && EINTR == errno); + + /* + * waitpid() cannot return 0 because we did not invoke it + * with the WNOHANG option. + */ + PR_ASSERT(0 != pid); + + /* + * The only possible error code is ECHILD. But if we do + * our accounting correctly, we should only call waitpid() + * when there is a child process to wait for. + */ + PR_ASSERT((pid_t) -1 != pid); + if ((pid_t) -1 == pid) { + break; + } + + PR_Lock(pr_wp.ml); + ProcessReapedChildInternal(pid, status); + pr_wp.numProcs--; + while (0 == pr_wp.numProcs) { + PR_WaitCondVar(pr_wp.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pr_wp.ml); + } + } +} + +#else /* _PR_NATIVE_THREADS */ + +static void WaitPidDaemonThread(void *unused) +{ + PRPollDesc pd; + PRFileDesc *fd; + int rv; + char buf[128]; + pid_t pid; + int status; +#ifdef _PR_SHARE_CLONES + struct pr_CreateProcOp *op; +#endif + +#ifdef _PR_SHARE_CLONES + pr_InstallSigchldHandler(); +#endif + + fd = PR_ImportFile(pr_wp.pipefd[0]); + PR_ASSERT(NULL != fd); + pd.fd = fd; + pd.in_flags = PR_POLL_READ; + + while (1) { + rv = PR_Poll(&pd, 1, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(1 == rv); + +#ifdef _PR_SHARE_CLONES + if (pr_waitpid_daemon_exit) { + return; + } + PR_Lock(pr_wp.ml); +#endif + + do { + rv = read(pr_wp.pipefd[0], buf, sizeof(buf)); + } while (sizeof(buf) == rv || (-1 == rv && EINTR == errno)); + +#ifdef _PR_SHARE_CLONES + PR_Unlock(pr_wp.ml); + while ((op = pr_wp.opHead) != NULL) { + op->process = ForkAndExec(op->path, op->argv, + op->envp, op->attr); + if (NULL == op->process) { + op->prerror = PR_GetError(); + op->oserror = PR_GetOSError(); + } + PR_Lock(pr_wp.ml); + pr_wp.opHead = op->next; + if (NULL == pr_wp.opHead) { + pr_wp.opTail = NULL; + } + op->done = PR_TRUE; + PR_NotifyCondVar(op->doneCV); + PR_Unlock(pr_wp.ml); + } +#endif + + while (1) { + do { + pid = waitpid((pid_t) -1, &status, WNOHANG); + } while ((pid_t) -1 == pid && EINTR == errno); + if (0 == pid) break; + if ((pid_t) -1 == pid) { + /* must be because we have no child processes */ + PR_ASSERT(ECHILD == errno); + break; + } + + PR_Lock(pr_wp.ml); + ProcessReapedChildInternal(pid, status); + PR_Unlock(pr_wp.ml); + } + } +} + +static void pr_SigchldHandler(int sig) +{ + int errnoCopy; + int rv; + + errnoCopy = errno; + + do { + rv = write(pr_wp.pipefd[1], "", 1); + } while (-1 == rv && EINTR == errno); + +#ifdef DEBUG + if (-1 == rv && EAGAIN != errno && EWOULDBLOCK != errno) { + char *msg = "cannot write to pipe\n"; + write(2, msg, strlen(msg) + 1); + _exit(1); + } +#endif + + errno = errnoCopy; +} + +static void pr_InstallSigchldHandler() +{ +#if defined(HPUX) && defined(_PR_DCETHREADS) +#error "HP-UX DCE threads have their own SIGCHLD handler" +#endif + + struct sigaction act, oact; + int rv; + + act.sa_handler = pr_SigchldHandler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NOCLDSTOP | SA_RESTART; + rv = sigaction(SIGCHLD, &act, &oact); + PR_ASSERT(0 == rv); + /* Make sure we are not overriding someone else's SIGCHLD handler */ +#ifndef _PR_SHARE_CLONES + PR_ASSERT(oact.sa_handler == SIG_DFL); +#endif +} + +#endif /* !defined(_PR_NATIVE_THREADS) */ + +static PRStatus _MD_InitProcesses() +{ +#if !defined(_PR_NATIVE_THREADS) + int rv; + int flags; +#endif +#ifdef SUNOS4 +#define _PR_NBIO_FLAG FNDELAY +#else +#define _PR_NBIO_FLAG O_NONBLOCK +#endif + + pr_wp.ml = PR_NewLock(); + PR_ASSERT(NULL != pr_wp.ml); + +#if defined(_PR_NATIVE_THREADS) + pr_wp.numProcs = 0; + pr_wp.cv = PR_NewCondVar(pr_wp.ml); + PR_ASSERT(NULL != pr_wp.cv); +#else + rv = pipe(pr_wp.pipefd); + PR_ASSERT(0 == rv); + flags = fcntl(pr_wp.pipefd[0], F_GETFL, 0); + fcntl(pr_wp.pipefd[0], F_SETFL, flags | _PR_NBIO_FLAG); + flags = fcntl(pr_wp.pipefd[1], F_GETFL, 0); + fcntl(pr_wp.pipefd[1], F_SETFL, flags | _PR_NBIO_FLAG); + +#ifndef _PR_SHARE_CLONES + pr_InstallSigchldHandler(); +#endif +#endif /* !_PR_NATIVE_THREADS */ + + pr_wp.thread = PR_CreateThread(PR_SYSTEM_THREAD, + WaitPidDaemonThread, NULL, PR_PRIORITY_NORMAL, +#ifdef _PR_SHARE_CLONES + PR_GLOBAL_THREAD, +#else + PR_LOCAL_THREAD, +#endif + PR_JOINABLE_THREAD, 0); + PR_ASSERT(NULL != pr_wp.thread); + + pr_wp.pidTable = (pr_PidRecord**)PR_CALLOC(NBUCKETS * sizeof(pr_PidRecord *)); + PR_ASSERT(NULL != pr_wp.pidTable); + return PR_SUCCESS; +} + +PRStatus _MD_DetachUnixProcess(PRProcess *process) +{ + PRStatus retVal = PR_SUCCESS; + pr_PidRecord *pRec; + + PR_Lock(pr_wp.ml); + pRec = FindPidTable(process->md.pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + if (NULL == pRec) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + retVal = PR_FAILURE; + goto done; + } + pRec->pid = process->md.pid; + pRec->state = _PR_PID_DETACHED; + pRec->reapedCV = NULL; + InsertPidTable(pRec); + } else { + PR_ASSERT(_PR_PID_REAPED == pRec->state); + if (_PR_PID_REAPED != pRec->state) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + retVal = PR_FAILURE; + } else { + DeletePidTable(pRec); + PR_ASSERT(NULL == pRec->reapedCV); + PR_DELETE(pRec); + } + } + +done: + PR_Unlock(pr_wp.ml); + return retVal; +} + +PRStatus _MD_WaitUnixProcess( + PRProcess *process, + PRInt32 *exitCode) +{ + pr_PidRecord *pRec; + PRStatus retVal = PR_SUCCESS; + PRBool interrupted = PR_FALSE; + + PR_Lock(pr_wp.ml); + pRec = FindPidTable(process->md.pid); + if (NULL == pRec) { + pRec = PR_NEW(pr_PidRecord); + if (NULL == pRec) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + retVal = PR_FAILURE; + goto done; + } + pRec->pid = process->md.pid; + pRec->state = _PR_PID_WAITING; + pRec->reapedCV = PR_NewCondVar(pr_wp.ml); + if (NULL == pRec->reapedCV) { + PR_DELETE(pRec); + retVal = PR_FAILURE; + goto done; + } + InsertPidTable(pRec); + while (!interrupted && _PR_PID_REAPED != pRec->state) { + if (PR_WaitCondVar(pRec->reapedCV, + PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE + && PR_GetError() == PR_PENDING_INTERRUPT_ERROR) { + interrupted = PR_TRUE; + } + } + if (_PR_PID_REAPED == pRec->state) { + if (exitCode) { + *exitCode = pRec->exitStatus; + } + } else { + PR_ASSERT(interrupted); + retVal = PR_FAILURE; + } + DeletePidTable(pRec); + PR_DestroyCondVar(pRec->reapedCV); + PR_DELETE(pRec); + } else { + PR_ASSERT(_PR_PID_REAPED == pRec->state); + PR_ASSERT(NULL == pRec->reapedCV); + DeletePidTable(pRec); + if (exitCode) { + *exitCode = pRec->exitStatus; + } + PR_DELETE(pRec); + } + +done: + PR_Unlock(pr_wp.ml); + return retVal; +} /* _MD_WaitUnixProcess */ + +PRStatus _MD_KillUnixProcess(PRProcess *process) +{ + PRErrorCode prerror; + PRInt32 oserror; + + if (kill(process->md.pid, SIGKILL) == 0) { + return PR_SUCCESS; + } + oserror = errno; + switch (oserror) { + case EPERM: + prerror = PR_NO_ACCESS_RIGHTS_ERROR; + break; + case ESRCH: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prerror, oserror); + return PR_FAILURE; +} /* _MD_KillUnixProcess */ diff --git a/pr/src/md/unix/uxwrap.c b/pr/src/md/unix/uxwrap.c new file mode 100644 index 00000000..9fc0fd3d --- /dev/null +++ b/pr/src/md/unix/uxwrap.c @@ -0,0 +1,518 @@ +/* -*- 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. + */ + +/* + *------------------------------------------------------------------------ + * File: uxwrap.c + * + * Our wrapped versions of the Unix select() and poll() system calls. + * + *------------------------------------------------------------------------ + */ + +#include "primpl.h" + +#if defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) +/* Do not wrap select() and poll(). */ +#else /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ +/* The include files for select() */ +#ifdef IRIX +#include <unistd.h> +#include <bstring.h> +#endif + +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> + +#define ZAP_SET(_to, _width) \ + PR_BEGIN_MACRO \ + memset(_to, 0, \ + ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \ + * sizeof(int) \ + ); \ + PR_END_MACRO + +#define COPY_SET(_to, _from, _width) \ + PR_BEGIN_MACRO \ + memcpy(_to, _from, \ + ((_width + 8*sizeof(int)-1) / (8*sizeof(int))) \ + * sizeof(int) \ + ); \ + PR_END_MACRO + +/* An internal global variable defined in prfile.c */ +extern PRIOMethods _pr_fileMethods; + + +/* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */ +static int _pr_xt_hack_fd = -1; + +int PR_XGetXtHackFD(void) +{ + int fds[2]; + + if (_pr_xt_hack_fd == -1) { + if (!pipe(fds)) { + _pr_xt_hack_fd = fds[0]; + } + } + return _pr_xt_hack_fd; + } + +static int (*_pr_xt_hack_okayToReleaseXLock)(void) = 0; + +void PR_SetXtHackOkayToReleaseXLockFn(int (*fn)(void)) +{ + _pr_xt_hack_okayToReleaseXLock = fn; +} + + +/* + *----------------------------------------------------------------------- + * select() -- + * + * Wrap up the select system call so that we can deschedule + * a thread that tries to wait for i/o. + * + *----------------------------------------------------------------------- + */ + +#if defined(HPUX9) +int select(size_t width, int *rl, int *wl, int *el, const struct timeval *tv) +#elif defined(AIX4_1) +int wrap_select(unsigned long width, void *rl, void *wl, void *el, + struct timeval *tv) +#elif (defined(BSDI) && !defined(BSDI_2)) +int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, + const struct timeval *tv) +#else +int select(int width, fd_set *rd, fd_set *wr, fd_set *ex, struct timeval *tv) +#endif +{ + int i; + int nfds; + int npds; + void *pollset; + PRPollDesc *pd; + PRFileDesc *prfd; + PRFilePrivate *secret; + PRIntervalTime timeout; + int retVal; +#if defined(HPUX9) || defined(AIX4_1) + fd_set *rd = (fd_set*) rl; + fd_set *wr = (fd_set*) wl; + fd_set *ex = (fd_set*) el; +#endif + fd_set r, w, x; + +#if 0 + /* + * Easy special case: zero timeout. Simply call the native + * select() with no fear of blocking. + */ + if (tv != NULL && tv->tv_sec == 0 && tv->tv_usec == 0) { +#if defined(HPUX9) || defined(AIX4_1) + return _MD_SELECT(width, rl, wl, el, tv); +#else + return _MD_SELECT(width, rd, wr, ex, tv); +#endif + } +#endif + + if (!_pr_initialized) + _PR_ImplicitInitialization(); + +#ifndef _PR_LOCAL_THREADS_ONLY + if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + nfds = _MD_SELECT(width, rd, wr, ex, tv); + return(nfds); + } +#endif + + if (width < 0 || width > FD_SETSIZE) { + errno = EINVAL; + return -1; + } + + /* Compute timeout */ + if (tv) { + /* + * These acceptable ranges for t_sec and t_usec are taken + * from the select() man pages. + */ + if (tv->tv_sec < 0 || tv->tv_sec > 100000000 + || tv->tv_usec < 0 || tv->tv_usec >= 1000000) { + errno = EINVAL; + return -1; + } + + /* Convert microseconds to ticks */ + timeout = PR_MicrosecondsToInterval(1000000*tv->tv_sec + tv->tv_usec); + } else { + /* tv being a NULL pointer means blocking indefinitely */ + timeout = PR_INTERVAL_NO_TIMEOUT; + } + + /* Check for no descriptors case (just doing a timeout) */ + if ((!rd && !wr && !ex) || !width) { + PR_Sleep(timeout); + return 0; + } + + if (rd) { COPY_SET(&r, rd, width); } + if (wr) { COPY_SET(&w, wr, width); } + if (ex) { COPY_SET(&x, ex, width); } + + /* + * Set up for PR_Poll(). The PRPollDesc array is allocated + * dynamically. If this turns out to have high performance + * penalty, one can change to use a large PRPollDesc array + * on the stack, and allocate dynamically only when it turns + * out to be not large enough. + * + * I allocate an array of size 'width', which is the maximum + * number of fds we may need to poll. + */ + pollset = PR_CALLOC(width * + (sizeof(PRPollDesc) + sizeof(PRFileDesc) + sizeof(PRFilePrivate))); + if (!pollset) { + errno = ENOMEM; + return -1; + } + pd = (PRPollDesc*)pollset; + prfd = (PRFileDesc*)(&pd[width]); + secret = (PRFilePrivate*)(&prfd[width]); + + for (npds = 0, i = 0; i < width; i++) { + int in_flags = 0; + if (rd && FD_ISSET(i, &r)) { + in_flags |= PR_POLL_READ; + } + if (wr && FD_ISSET(i, &w)) { + in_flags |= PR_POLL_WRITE; + } + if (ex && FD_ISSET(i, &x)) { + in_flags |= PR_POLL_EXCEPT; + } + if (in_flags) { + prfd[npds].secret = &secret[npds]; + prfd[npds].secret->state = _PR_FILEDESC_OPEN; + prfd[npds].secret->md.osfd = i; + prfd[npds].methods = &_pr_fileMethods; + + pd[npds].fd = &prfd[npds]; + pd[npds].in_flags = in_flags; + pd[npds].out_flags = 0; + npds += 1; + } + } + + /* see comments in ns/cmd/xfe/mozilla.c (look for "PR_XGetXtHackFD") */ + { + + int needToLockXAgain; + + needToLockXAgain = 0; + if (rd && (_pr_xt_hack_fd != -1) && + FD_ISSET(_pr_xt_hack_fd, &r) && PR_XIsLocked() && + (!_pr_xt_hack_okayToReleaseXLock || _pr_xt_hack_okayToReleaseXLock())) { + PR_XUnlock(); + needToLockXAgain = 1; + } + + /* This is the potentially blocking step */ + retVal = PR_Poll(pd, npds, timeout); + + if (needToLockXAgain) { + PR_XLock(); + } + } + + if (retVal > 0) + { + /* Compute select results */ + if (rd) ZAP_SET(rd, width); + if (wr) ZAP_SET(wr, width); + if (ex) ZAP_SET(ex, width); + + /* + * The return value can be either the number of ready file + * descriptors or the number of set bits in the three fd_set's. + */ + retVal = 0; /* we're going to recompute */ + for (i = 0; i < npds; ++i, pd++) + { + if (pd->out_flags) { + int nbits = 0; /* The number of set bits on for this fd */ + + if (pd->out_flags & PR_POLL_NVAL) { + errno = EBADF; + PR_LOG(_pr_io_lm, PR_LOG_ERROR, + ("select returns EBADF for %d", pd->fd)); + retVal = -1; + break; + } + if (rd && (pd->out_flags & PR_POLL_READ)) { + FD_SET(pd->fd->secret->md.osfd, rd); + nbits++; + } + if (wr && (pd->out_flags & PR_POLL_WRITE)) { + FD_SET(pd->fd->secret->md.osfd, wr); + nbits++; + } + if (ex && (pd->out_flags & PR_POLL_EXCEPT)) { + FD_SET(pd->fd->secret->md.osfd, ex); + nbits++; + } + PR_ASSERT(nbits > 0); +#if defined(HPUX) || defined(SOLARIS) || defined(SUNOS4) || defined(OSF1) || defined(AIX) + retVal += nbits; +#else /* IRIX */ + retVal += 1; +#endif + } + } + } + + PR_ASSERT(tv || retVal != 0); + PR_LOG(_pr_io_lm, PR_LOG_MIN, ("select returns %d", retVal)); + PR_DELETE(pollset); + + return retVal; +} + +/* + * Linux, BSDI, and FreeBSD don't have poll() + */ + +#if !defined(LINUX) && !defined(FREEBSD) && !defined(BSDI) + +/* + *----------------------------------------------------------------------- + * poll() -- + * + * RETURN VALUES: + * -1: fails, errno indicates the error. + * 0: timed out, the revents bitmasks are not set. + * positive value: the number of file descriptors for which poll() + * has set the revents bitmask. + * + *----------------------------------------------------------------------- + */ + +#include <poll.h> + +#if defined(AIX4_1) +int wrap_poll(void *listptr, unsigned long nfds, long timeout) +#elif (defined(AIX) && !defined(AIX4_1)) +int poll(void *listptr, unsigned long nfds, long timeout) +#elif defined(OSF1) || (defined(HPUX) && !defined(HPUX9)) +int poll(struct pollfd filedes[], unsigned int nfds, int timeout) +#elif defined(HPUX9) +int poll(struct pollfd filedes[], int nfds, int timeout) +#else +int poll(struct pollfd *filedes, unsigned long nfds, int timeout) +#endif +{ +#ifdef AIX + struct pollfd *filedes = (struct pollfd *) listptr; +#endif + void *pollset; + PRPollDesc *pd; + PRFileDesc *prfd; + PRFilePrivate *secret; + int i; + PRUint32 ticks; + PRInt32 retVal; + + /* + * Easy special case: zero timeout. Simply call the native + * poll() with no fear of blocking. + */ + if (timeout == 0) { +#if defined(AIX) + return _MD_POLL(listptr, nfds, timeout); +#else + return _MD_POLL(filedes, nfds, timeout); +#endif + } + + if (!_pr_initialized) { + _PR_ImplicitInitialization(); + } + +#ifndef _PR_LOCAL_THREADS_ONLY + if (_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) { + retVal = _MD_POLL(filedes, nfds, timeout); + return(retVal); + } +#endif + + /* We do not support the pollmsg structures on AIX */ +#ifdef AIX + PR_ASSERT((nfds & 0xff00) == 0); +#endif + + if (timeout < 0 && timeout != -1) { + errno = EINVAL; + return -1; + } + + /* Convert timeout from miliseconds to ticks */ + if (timeout == -1) { + ticks = PR_INTERVAL_NO_TIMEOUT; + } else if (timeout == 0) { + ticks = PR_INTERVAL_NO_WAIT; + } else { + ticks = PR_MillisecondsToInterval(timeout); + } + + /* Check for no descriptor case (just do a timeout) */ + if (nfds == 0) { + PR_Sleep(ticks); + return 0; + } + + pollset = PR_CALLOC(nfds * + (sizeof(PRPollDesc) + sizeof(PRFileDesc) + sizeof(PRFilePrivate))); + if (!pollset) { + errno = EAGAIN; + return -1; + } + pd = (PRPollDesc*)pollset; + prfd = (PRFileDesc*)(&pd[nfds]); + secret = (PRFilePrivate*)(&prfd[nfds]); + + for (i = 0; i < nfds; i++) { + prfd[i].secret = &secret[i]; + prfd[i].secret->state = _PR_FILEDESC_OPEN; + prfd[i].secret->md.osfd = filedes[i].fd; + prfd[i].methods = &_pr_fileMethods; + + pd[i].fd = &prfd[i]; + pd[i].out_flags = 0; + + /* + * poll() ignores negative fd's. We emulate this behavior + * by making sure the in_flags for a negative fd is zero. + */ + if (filedes[i].fd < 0) { + pd[i].in_flags = 0; + continue; + } +#ifdef _PR_USE_POLL + pd[i].in_flags = filedes[i].events; +#else + /* + * Map the native poll flags to nspr20 poll flags. + * POLLIN, POLLRDNORM ===> PR_POLL_READ + * POLLOUT, POLLWRNORM ===> PR_POLL_WRITE + * POLLPRI, POLLRDBAND ===> PR_POLL_EXCEPT + * POLLNORM, POLLWRBAND (and POLLMSG on some platforms) + * are ignored. + * + * The output events POLLERR and POLLHUP are never turned on. + * POLLNVAL may be turned on. + */ + pd[i].in_flags = 0; + if (filedes[i].events & (POLLIN +#ifdef POLLRDNORM + | POLLRDNORM +#endif + )) { + pd[i].in_flags |= PR_POLL_READ; + } + if (filedes[i].events & (POLLOUT +#ifdef POLLWRNORM + | POLLWRNORM +#endif + )) { + pd[i].in_flags |= PR_POLL_WRITE; + } + if (filedes[i].events & (POLLPRI +#ifdef POLLRDBAND + | POLLRDBAND +#endif + )) { + pd[i].in_flags |= PR_POLL_EXCEPT; + } +#endif /* _PR_USE_POLL */ + } + + retVal = PR_Poll(pd, nfds, ticks); + + if (retVal > 0) { + /* Set the revents bitmasks */ + for (i = 0; i < nfds; i++) { + PR_ASSERT(filedes[i].fd >= 0 || pd[i].in_flags == 0); + if (filedes[i].fd < 0) { + continue; /* skip negative fd's */ + } +#ifdef _PR_USE_POLL + filedes[i].revents = pd[i].out_flags; +#else + filedes[i].revents = 0; + if (0 == pd[i].out_flags) { + continue; + } + if (pd[i].out_flags & PR_POLL_READ) { + if (filedes[i].events & POLLIN) + filedes[i].revents |= POLLIN; +#ifdef POLLRDNORM + if (filedes[i].events & POLLRDNORM) + filedes[i].revents |= POLLRDNORM; +#endif + } + if (pd[i].out_flags & PR_POLL_WRITE) { + if (filedes[i].events & POLLOUT) + filedes[i].revents |= POLLOUT; +#ifdef POLLWRNORM + if (filedes[i].events & POLLWRNORM) + filedes[i].revents |= POLLWRNORM; +#endif + } + if (pd[i].out_flags & PR_POLL_EXCEPT) { + if (filedes[i].events & POLLPRI) + filedes[i].revents |= POLLPRI; +#ifdef POLLRDBAND + if (filedes[i].events & POLLRDBAND) + filedes[i].revents |= POLLRDBAND; +#endif + } + if (pd[i].out_flags & PR_POLL_ERR) { + filedes[i].revents |= POLLERR; + } + if (pd[i].out_flags & PR_POLL_NVAL) { + filedes[i].revents |= POLLNVAL; + } +#endif /* _PR_USE_POLL */ + } + } + + PR_DELETE(pollset); + + return retVal; +} + +#endif /* !defined(LINUX) */ + +#endif /* defined(_PR_PTHREADS) || defined(_PR_GLOBAL_THREADS_ONLY) */ + +/* uxwrap.c */ + 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; + } +} diff --git a/pr/src/memory/Makefile b/pr/src/memory/Makefile new file mode 100644 index 00000000..017c59e2 --- /dev/null +++ b/pr/src/memory/Makefile @@ -0,0 +1,42 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = prseg.c + +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/memory/prseg.c b/pr/src/memory/prseg.c new file mode 100644 index 00000000..6a95bd53 --- /dev/null +++ b/pr/src/memory/prseg.c @@ -0,0 +1,192 @@ +/* -*- 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" + +void _PR_InitSegs(void) +{ + _PR_MD_INIT_SEGS(); +} + +/* +** Allocate a memory segment. The size value is rounded up to the native +** system page size and a page aligned portion of memory is returned. +** This memory is not part of the malloc heap. If "vaddr" is not NULL +** then PR tries to allocate the segment at the desired virtual address. +*/ +PR_IMPLEMENT(PRSegment*) PR_NewSegment(PRUint32 size, void *vaddr) +{ + PRSegment *seg; + + /* calloc the data structure for the segment */ + seg = PR_NEWZAP(PRSegment); + + if (seg) { + size = ((size + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift; + /* + ** Now, allocate the actual segment memory (or map under some OS) + ** The OS specific code decides from where or how to allocate memory. + */ + if (_PR_MD_ALLOC_SEGMENT(seg, size, vaddr) != PR_SUCCESS) { + PR_DELETE(seg); + return NULL; + } + } + + return seg; +} + +/* +** Free a memory segment. +*/ +PR_IMPLEMENT(void) PR_DestroySegment(PRSegment *seg) +{ + _PR_MD_FREE_SEGMENT(seg); + PR_DELETE(seg); +} + +/* XXX Fix the following to make machine-independent */ +#ifdef XP_UNIX +#include <sys/mman.h> +#endif + +#ifndef PROT_NONE +#define PROT_NONE 0 +#endif + +extern PRInt32 _pr_zero_fd; + +/* +** Attempt to grow/shrink the given segment. +*/ +PR_IMPLEMENT(PRUint32) PR_GrowSegment(PRSegment *seg, PRInt32 delta) +{ + char *oldend, *newend; +#if 0 +#ifdef XP_UNIX + int prot; + void *rv = (void *) -1; +#endif +#endif /* 0 */ + + if (!(seg->flags & _PR_SEG_VM)) { + return 0; + } + + oldend = (char*)seg->vaddr + seg->size; + if (delta > 0) { +#if 0 + + /* + * CANNOT use MAP_FIXED because it will replace any existing mappings + */ + /* Growing the segment */ + delta = ((delta + _pr_pageSize - 1) >> _pr_pageShift) << _pr_pageShift; + newend = oldend + delta; +#ifdef XP_UNIX + prot = PROT_READ|PROT_WRITE; + rv = mmap(oldend, delta, prot, +#ifdef OSF1 + /* XXX need to pick a vaddr to use */ + MAP_PRIVATE|MAP_FIXED, +#else + MAP_SHARED|MAP_FIXED, +#endif + _pr_zero_fd, 0); +#endif /* XP_UNIX */ + if (rv == (void*)-1) { + /* Can't extend the heap */ + return 0; + } + seg->size = seg->size + delta; +#endif /* 0 */ + } else if (delta < 0) { + /* Shrinking the segment */ + delta = -delta; + delta = (delta >> _pr_pageShift) << _pr_pageShift; /* trunc */ + if ((PRUint32)delta >= seg->size) { + /* XXX what to do? */ + return 0; + } + newend = oldend - delta; + if (newend < oldend) { +#ifdef XP_UNIX + (void) munmap(oldend, newend - oldend); + seg->size = seg->size + delta; +#endif + } + } + return delta; +} + +/* +** Change the mapping on a segment. +** "how" == PR_SEGMENT_NONE: the segment becomes unmapped +** "how" == PR_SEGMENT_RDONLY: the segment becomes mapped and readable +** "how" == PR_SEGMENT_RDWR: the segment becomes mapped read/write +** +** If a segment can be read then it is also possible to execute code in +** it. +*/ +PR_IMPLEMENT(void) PR_MapSegment(PRSegment *seg, PRSegmentAccess how) +{ + if (seg->access == how) return; + seg->access = how; + +#ifdef XP_UNIX + if (seg->flags & _PR_SEG_VM) { + int prot; + switch (how) { + case PR_SEGMENT_NONE: + prot = PROT_NONE; + break; + case PR_SEGMENT_RDONLY: + prot = PROT_READ; + break; + case PR_SEGMENT_RDWR: + prot = PROT_READ|PROT_WRITE; + break; + } + (void) mprotect(seg->vaddr, seg->size, prot); + } +#endif +} + +/* +** Return the size of the segment +*/ +PR_IMPLEMENT(size_t) PR_GetSegmentSize(PRSegment *seg) +{ + return seg->size; +} + +/* +** Return the virtual address of the segment +*/ +PR_IMPLEMENT(void*) PR_GetSegmentVaddr(PRSegment *seg) +{ + return seg->vaddr; +} + +/* +** Return a segments current access rights +*/ +PR_IMPLEMENT(PRSegmentAccess) PR_GetSegmentAccess(PRSegment *seg) +{ + return seg->access; +} diff --git a/pr/src/misc/Makefile b/pr/src/misc/Makefile new file mode 100644 index 00000000..015fc259 --- /dev/null +++ b/pr/src/misc/Makefile @@ -0,0 +1,64 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + pralarm.c \ + pratom.c \ + prdtoa.c \ + prenv.c \ + prerror.c \ + prinit.c \ + prinrval.c \ + prlog2.c \ + prlong.c \ + prnetdb.c \ + prsystem.c \ + prtime.c \ + prthinfo.c \ + $(NULL) + +TARGETS = $(OBJS) + +INCLUDES = -I$(DIST)/include/private -I$(DIST)/include + +include $(MOD_DEPTH)/config/rules.mk + +# An AIX Optimization bug causes PR_dtoa() to produce wrong result. +# This suppresses optimization for this single compilation unit. +ifeq ($(OS_ARCH), AIX) +$(OBJDIR)/prdtoa.o: + @$(MAKE_OBJDIR) + $(CC) -o $@ -c $(filter-out -O, $(CFLAGS)) prdtoa.c +endif + +export:: $(TARGETS) + +install:: export + diff --git a/pr/src/misc/pralarm.c b/pr/src/misc/pralarm.c new file mode 100644 index 00000000..8f518943 --- /dev/null +++ b/pr/src/misc/pralarm.c @@ -0,0 +1,263 @@ +/* -*- 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" + +/**********************************************************************/ +/******************************* PRALARM ******************************/ +/**********************************************************************/ + +#ifdef XP_MAC +#include "pralarm.h" +#else +#include "obsolete/pralarm.h" +#endif + +struct PRAlarmID { /* typedef'd in pralarm.h */ + PRCList list; /* circular list linkage */ + PRAlarm *alarm; /* back pointer to owning alarm */ + PRPeriodicAlarmFn function; /* function to call for notify */ + void *clientData; /* opaque client context */ + PRIntervalTime period; /* the client defined period */ + PRUint32 rate; /* rate of notification */ + + PRUint32 accumulator; /* keeps track of # notifies */ + PRIntervalTime epoch; /* when timer was started */ + PRIntervalTime nextNotify; /* when we'll next do our thing */ + PRIntervalTime lastNotify; /* when we last did our thing */ +}; + +typedef enum {alarm_active, alarm_inactive} _AlarmState; + +struct PRAlarm { /* typedef'd in pralarm.h */ + PRCList timers; /* base of alarm ids list */ + PRLock *lock; /* lock used to protect data */ + PRCondVar *cond; /* condition that used to wait */ + PRThread *notifier; /* thread to deliver notifies */ + PRAlarmID *current; /* current alarm being served */ + _AlarmState state; /* used to delete the alarm */ +}; + +static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id) +{ +/* + * Puts 'id' back into the sorted list iff it's not NULL. + * Removes the first element from the list and returns it (or NULL). + * List is "assumed" to be short. + * + * NB: Caller is providing locking + */ + PRCList *timer; + PRAlarmID *result = id; + PRIntervalTime now = PR_IntervalNow(); + + if (!PR_CLIST_IS_EMPTY(&alarm->timers)) + { + if (id != NULL) /* have to put this id back in */ + { + PRIntervalTime idDelta = now - id->nextNotify; + timer = &alarm->timers; + do + { + result = (PRAlarmID*)timer; + if ((PRIntervalTime)(now - result->nextNotify) > idDelta) + { + PR_INSERT_BEFORE(&id->list, &alarm->timers); + break; + } + timer = timer->next; + } while (timer != alarm->timers.next); + } + result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers)); + PR_REMOVE_LINK(timer); /* remove it from the list */ + } + + return result; +} /* pr_getNextAlarm */ + +static PRIntervalTime pr_PredictNextNotifyTime(PRAlarmID *id) +{ + PRIntervalTime delta; + PRFloat64 baseRate = (PRFloat64)id->period / (PRFloat64)id->rate; + PRFloat64 offsetFromEpoch = (PRFloat64)id->accumulator * baseRate; + + id->accumulator += 1; /* every call advances to next period */ + id->lastNotify = id->nextNotify; /* just keeping track of things */ + id->nextNotify = (PRIntervalTime)(offsetFromEpoch + 0.5); + + delta = id->nextNotify - id->nextNotify; + return delta; +} /* pr_PredictNextNotifyTime */ + +static void PR_CALLBACK pr_alarmNotifier(void *arg) +{ + /* + * This is the root of the notifier thread. There is one such thread + * for each PRAlarm. It may service an arbitrary (though assumed to be + * small) number of alarms using the same thread and structure. It + * continues to run until the alarm is destroyed. + */ + PRAlarmID *id = NULL; + PRAlarm *alarm = (PRAlarm*)arg; + enum {notify, abort, scan} why = scan; + + while (why != abort) + { + PRIntervalTime pause; + + PR_Lock(alarm->lock); + while (why == scan) + { + alarm->current = NULL; /* reset current id */ + if (alarm->state == alarm_inactive) why = abort; /* we're toast */ + else if (why == scan) /* the dominant case */ + { + id = pr_getNextAlarm(alarm, id); /* even if it's the same */ + if (id == NULL) /* there are no alarms set */ + (void)PR_WaitCondVar(alarm->cond, PR_INTERVAL_NO_TIMEOUT); + else + { + pause = id->nextNotify - (PR_IntervalNow() - id->epoch); + if ((PRInt32)pause <= 0) /* is this one's time up? */ + { + why = notify; /* set up to do our thing */ + alarm->current = id; /* id we're about to schedule */ + } + else + (void)PR_WaitCondVar(alarm->cond, pause); /* dally */ + } + } + } + PR_Unlock(alarm->lock); + + if (why == notify) + { + (void)pr_PredictNextNotifyTime(id); + if (!id->function(id, id->clientData, ~pause)) + { + /* + * Notified function decided not to continue. Free + * the alarm id to make sure it doesn't get back on + * the list. + */ + PR_DELETE(id); /* free notifier object */ + id = NULL; /* so it doesn't get back into the list */ + } + why = scan; /* so we can cycle through the loop again */ + } + } + +} /* pr_alarm_notifier */ + +PR_IMPLEMENT(PRAlarm*) PR_CreateAlarm() +{ + PRAlarm *alarm = PR_NEWZAP(PRAlarm); + if (alarm != NULL) + { + if ((alarm->lock = PR_NewLock()) == NULL) goto done; + if ((alarm->cond = PR_NewCondVar(alarm->lock)) == NULL) goto done; + alarm->state = alarm_active; + PR_INIT_CLIST(&alarm->timers); + alarm->notifier = PR_CreateThread( + PR_USER_THREAD, pr_alarmNotifier, alarm, + PR_GetThreadPriority(PR_GetCurrentThread()), + PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + if (alarm->notifier == NULL) goto done; + } + return alarm; + +done: + if (alarm->cond != NULL) PR_DestroyCondVar(alarm->cond); + if (alarm->lock != NULL) PR_DestroyLock(alarm->lock); + PR_DELETE(alarm); + return NULL; +} /* CreateAlarm */ + +PR_IMPLEMENT(PRStatus) PR_DestroyAlarm(PRAlarm *alarm) +{ + PRStatus rv; + + PR_Lock(alarm->lock); + alarm->state = alarm_inactive; + rv = PR_NotifyCondVar(alarm->cond); + PR_Unlock(alarm->lock); + + if (rv == PR_SUCCESS) + rv = PR_JoinThread(alarm->notifier); + if (rv == PR_SUCCESS) + { + PR_DestroyCondVar(alarm->cond); + PR_DestroyLock(alarm->lock); + PR_DELETE(alarm); + } + return rv; +} /* PR_DestroyAlarm */ + +PR_IMPLEMENT(PRAlarmID*) PR_SetAlarm( + PRAlarm *alarm, PRIntervalTime period, PRUint32 rate, + PRPeriodicAlarmFn function, void *clientData) +{ + /* + * Create a new periodic alarm an existing current structure. + * Set up the context and compute the first notify time (immediate). + * Link the new ID into the head of the list (since it's notifying + * immediately). + */ + + PRAlarmID *id = PR_NEWZAP(PRAlarmID); + + if (!id) + return NULL; + + id->alarm = alarm; + PR_INIT_CLIST(&id->list); + id->function = function; + id->clientData = clientData; + id->period = period; + id->rate = rate; + id->epoch = id->nextNotify = PR_IntervalNow(); + (void)pr_PredictNextNotifyTime(id); + + PR_Lock(alarm->lock); + PR_INSERT_BEFORE(&id->list, &alarm->timers); + PR_NotifyCondVar(alarm->cond); + PR_Unlock(alarm->lock); + + return id; +} /* PR_SetAlarm */ + +PR_IMPLEMENT(PRStatus) PR_ResetAlarm( + PRAlarmID *id, PRIntervalTime period, PRUint32 rate) +{ + /* + * Can only be called from within the notify routine. Doesn't + * need locking because it can only be called from within the + * notify routine. + */ + if (id != id->alarm->current) + return PR_FAILURE; + id->period = period; + id->rate = rate; + id->accumulator = 1; + id->epoch = PR_IntervalNow(); + (void)pr_PredictNextNotifyTime(id); + return PR_SUCCESS; +} /* PR_ResetAlarm */ + + + diff --git a/pr/src/misc/pratom.c b/pr/src/misc/pratom.c new file mode 100644 index 00000000..c7eccae0 --- /dev/null +++ b/pr/src/misc/pratom.c @@ -0,0 +1,105 @@ +/* -*- 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. + */ + +/* +** PR Atomic operations +*/ + + +#include "pratom.h" +#include "primpl.h" + +/* + * The following is a fallback implementation that emulates + * emulates atomic operations for platforms without atomic + * operations. If a platform has atomic operations, + * it should define the macro _PR_HAVE_ATOMIC_OPS, and + * the following will not be compiled in. + */ + +#ifndef _PR_HAVE_ATOMIC_OPS + +/* + * We use a single lock for all the emulated atomic operations. + * The lock contention should be acceptable. + */ + +static PRLock *monitor = NULL; +void _PR_MD_INIT_ATOMIC() +{ + if (monitor == NULL) { + monitor = PR_NewLock(); + } +} + +PR_IMPLEMENT(PRInt32) +_PR_MD_ATOMIC_INCREMENT(PRInt32 *val) +{ + PRInt32 rv; + PR_Lock(monitor); + rv = ++(*val); + PR_Unlock(monitor); + return rv; +} + +PR_IMPLEMENT(PRInt32) +_PR_MD_ATOMIC_DECREMENT(PRInt32 *val) +{ + PRInt32 rv; + PR_Lock(monitor); + rv = --(*val); + PR_Unlock(monitor); + return rv; +} + +PR_IMPLEMENT(PRInt32) +_PR_MD_ATOMIC_SET(PRInt32 *val, PRInt32 newval) +{ + PRInt32 rv; + PR_Lock(monitor); + rv = *val; + *val = newval; + PR_Unlock(monitor); + return rv; +} + +#endif /* !_PR_HAVE_ATOMIC_OPS */ + +void _PR_InitAtomic(void) +{ + _PR_MD_INIT_ATOMIC(); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicIncrement(PRInt32 *val) +{ + return _PR_MD_ATOMIC_INCREMENT(val); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicDecrement(PRInt32 *val) +{ + return _PR_MD_ATOMIC_DECREMENT(val); +} + +PR_IMPLEMENT(PRInt32) +PR_AtomicSet(PRInt32 *val, PRInt32 newval) +{ + return _PR_MD_ATOMIC_SET(val, newval); +} + diff --git a/pr/src/misc/prdtoa.c b/pr/src/misc/prdtoa.c new file mode 100644 index 00000000..a9253617 --- /dev/null +++ b/pr/src/misc/prdtoa.c @@ -0,0 +1,3189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ + +#include "primpl.h" + +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991 by AT&T. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR AT&T MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to + David M. Gay + AT&T Bell Laboratories, Room 2C-463 + 600 Mountain Avenue + Murray Hill, NJ 07974-2070 + U.S.A. + dmg@research.att.com or research!dmg + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic. + * #define Unsigned_Shifts if >> does treats its left operand as unsigned. + * #define No_leftright to omit left-right logic in fast floating-point + * computation of PR_dtoa. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define Just_16 to store 16 bits per 32-bit Long when doing high-precision + * integer arithmetic. Whether this speeds things up or slows things + * down depends on the machine and the number being converted. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. + */ +#if defined(IS_LITTLE_ENDIAN) +#define IEEE_8087 +#else +#define IEEE_MC68k +#endif + +#ifndef Long +#if PR_BYTES_PER_LONG == 4 +#define Long long +#elif PR_BYTES_PER_INT == 4 +#define Long int +#else +#error "No suitable type for Long" +#endif +#endif + +#ifdef DEBUG_DTOA +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#else +#define Bug(x) +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef MALLOC +extern void *MALLOC(size_t); +#else +#define MALLOC PR_MALLOC +#endif + +#include "errno.h" +#ifdef Bad_float_h +#undef __STDC__ +#ifdef IEEE_MC68k +#define IEEE_ARITHMETIC +#endif +#ifdef IEEE_8087 +#define IEEE_ARITHMETIC +#endif + +#ifdef IEEE_ARITHMETIC +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7976931348623157e+308 +#endif + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define FLT_ROUNDS 0 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif +#else +#include "float.h" +#endif +#ifndef __MATH_H__ +#include "math.h" +#endif + +#ifndef CONST +#define CONST const +#endif + +#ifdef Unsigned_Shifts +#define Sign_Extend(a,b) if (b < 0) a |= 0xffff0000; +#else +#define Sign_Extend(a,b) /*no-op*/ +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +#ifdef IEEE_8087 +#define word0(x) ((unsigned Long *)&x)[1] +#define word1(x) ((unsigned Long *)&x)[0] +#else +#define word0(x) ((unsigned Long *)&x)[0] +#define word1(x) ((unsigned Long *)&x)[1] +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#if defined(IEEE_8087) + defined(IEEE_MC68k) +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define IEEE_Arith +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ +#else +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif +#endif + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Just_16 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#ifndef Pack_32 +#define Pack_32 +#endif +#endif + +#define Kmax 15 + +/* + * Note: if you ever change struct Bigint, make sure that the + * definition of the Bcopy(x,y) macro is still correct. + */ +struct Bigint { + struct Bigint *next; + PRInt32 k, maxwds, sign, wds; + unsigned Long x[1]; +}; + +typedef struct Bigint Bigint; + +static Bigint *freelist[Kmax+1]; + +static PRLock *freelist_lock; + +static Bigint *Balloc(PRInt32 k) +{ + PRInt32 x; + Bigint *rv; + + PR_Lock(freelist_lock); + if ((rv = freelist[k]) != NULL) { + freelist[k] = rv->next; + } + PR_Unlock(freelist_lock); + if (rv == NULL) { + x = 1 << k; + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(Long)); + rv->k = k; + rv->maxwds = x; + } + rv->sign = rv->wds = 0; + return rv; +} + +static void Bfree (Bigint *v) +{ + if (v) { + PR_Lock(freelist_lock); + v->next = freelist[v->k]; + freelist[v->k] = v; + PR_Unlock(freelist_lock); + } +} + +/* + * The definition of the Bcopy macro is highly dependent on the + * ordering of members in struct Bigint. + */ +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ + y->wds*sizeof(Long) + 2*sizeof(PRInt32)) + +static Bigint *multadd(Bigint *b, PRInt32 m, PRInt32 a) /* multiply by m and add a */ +{ + PRInt32 i, wds; + unsigned Long *x, y; +#ifdef Pack_32 + unsigned Long xi, z; +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + do { +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + a; + z = (xi >> 16) * m + (y >> 16); + a = (PRInt32)(z >> 16); + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + a; + a = (PRInt32)(y >> 16); + *x++ = y & 0xffff; +#endif + } + while(++i < wds); + if (a) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = a; + b->wds = wds; + } + return b; +} + +static Bigint *s2b(CONST char *s, PRInt32 nd0, PRInt32 nd, unsigned Long y9) +{ + Bigint *b; + PRInt32 i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s++; + } + else + s += 10; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; +} + +static PRInt32 hi0bits(register unsigned Long x) +{ + register PRInt32 k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} + +static PRInt32 lo0bits(unsigned Long *y) +{ + register PRInt32 k; + register unsigned Long x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x & 1) + return 32; + } + *y = x; + return k; +} + +static Bigint *i2b(PRInt32 i) +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; +} + +static Bigint *mult(CONST Bigint *a, CONST Bigint *b) +{ + CONST Bigint *t; + Bigint *c; + PRInt32 k, wa, wb, wc; + unsigned Long carry, y, z; + unsigned Long *xc, *xc0, *xce; + CONST unsigned Long *x, *xa, *xae, *xb, *xbe; +#ifdef Pack_32 + unsigned Long z2; +#endif + + if (a->wds < b->wds) { + t = a; + a = b; + b = t; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(xc = c->x, xce = xc + wc; xc < xce; xc++) + *xc = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if ((y = *xb & 0xffff) != 0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if ((y = *xb >> 16) != 0) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +/* + * 'p5s' points to a linked list of Bigints that are powers of 5. + * This list grows on demand, and it can only grow: it won't change + * in any other way. So if we read 'p5s' or the 'next' field of + * some Bigint on the list, and it is not NULL, we know it won't + * change to NULL or some other value. Only when the value of + * 'p5s' or 'next' is NULL do we need to acquire the lock and add + * a new Bigint to the list. + */ + +static Bigint *p5s; + +static PRLock *p5s_lock; + +static Bigint *pow5mult(Bigint *b, PRInt32 k) +{ + Bigint *b1, *p5, *p51; + PRInt32 i; + static CONST PRInt32 p05[3] = { 5, 25, 125 }; + + if ((i = k & 3) != 0) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* + * We take great care to not call i2b() and Bfree() + * while holding the lock. + */ + Bigint *wasted_effort = NULL; + p5 = i2b(625); + /* lock and check again */ + PR_Lock(p5s_lock); + if (!p5s) { + /* first time */ + p5s = p5; + p5->next = 0; + } else { + /* some other thread just beat us */ + wasted_effort = p5; + p5 = p5s; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { + Bigint *wasted_effort = NULL; + p51 = mult(p5, p5); + PR_Lock(p5s_lock); + if (!p5->next) { + p5->next = p51; + p51->next = 0; + } else { + wasted_effort = p51; + p51 = p5->next; + } + PR_Unlock(p5s_lock); + if (wasted_effort) { + Bfree(wasted_effort); + } + } + p5 = p51; + } + return b; +} + +static Bigint *lshift(Bigint *b, PRInt32 k) +{ + PRInt32 i, k1, n, n1; + Bigint *b1; + unsigned Long *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z) != 0) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z) != 0) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + +static PRInt32 cmp(Bigint *a, Bigint *b) +{ + unsigned Long *xa, *xa0, *xb, *xb0; + PRInt32 i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG_DTOA + if ((i > 1 && !a->x[i-1])) + Bug("cmp called with a->x[a->wds-1] == 0"); + if ((j > 1 && !b->x[j-1])) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + +static Bigint *diff(Bigint *a, Bigint *b) +{ + Bigint *c; + PRInt32 i, wa, wb; + Long borrow, y; /* We need signed shifts here. */ + unsigned Long *xa, *xae, *xb, *xbe, *xc; +#ifdef Pack_32 + Long z; +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef Pack_32 + do { + y = (long)((*xa & 0xffff) - (*xb & 0xffff) + borrow); + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (long)((*xa++ >> 16) - (*xb++ >> 16) + borrow); + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (long)((*xa & 0xffff) + borrow); + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (long)((*xa++ >> 16) + borrow); + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *xc++ = y & 0xffff; + } +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; +} + +static double ulp(double x) +{ + register Long L; + double a; + + L = (long)((word0(x) & Exp_mask) - (P-1)*Exp_msk1); +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(a) = L; + word1(a) = 0; +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(a) = 0x80000 >> L; + word1(a) = 0; + } + else { + word0(a) = 0; + L -= Exp_shift; + word1(a) = L >= 31 ? 1 : 1 << (31 - L); + } + } +#endif + return a; +} + +static double +b2d +#ifdef KR_headers +(a, e) Bigint *a; PRInt32 *e; +#else +(Bigint *a, PRInt32 *e) +#endif +{ + unsigned Long *xa, *xa0, w, y, z; + PRInt32 k; + double d; +#ifdef VAX + unsigned Long d0, d1; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG_DTOA + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << (32 - Ebits + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif +ret_d: +#ifdef VAX + word0(d) = d0 >> 16 | d0 << 16; + word1(d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return d; +} + +static Bigint * +d2b +#ifdef KR_headers +(d, e, bits) double d; PRInt32 *e, *bits; +#else +(double d, PRInt32 *e, PRInt32 *bits) +#endif +{ + Bigint *b; + PRInt32 de, i, k; + unsigned Long *x, y, z; +#ifdef VAX + unsigned Long d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (PRInt32)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (PRInt32)(d0 >> Exp_shift)) != 0) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1) != 0) { + if ((k = lo0bits(&y)) != 0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; + i = b->wds = (x[1] = z) ? 2 : 1; + } + else { +#ifdef DEBUG_DTOA + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + x[0] = z; + i = b->wds = 1; + k += 32; + } +#else + if ((y = d1) != 0) { + if ((k = lo0bits(&y)) != 0) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG_DTOA + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +} +#undef d0 +#undef d1 + +static double +ratio +#ifdef KR_headers +(a, b) Bigint *a, *b; +#else +(Bigint *a, Bigint *b) +#endif +{ + double da, db; + PRInt32 k, ka, kb; + + da = b2d(a, &ka); + db = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(da) += (k >> 2)*Exp_msk1; + if (k &= 3) + da *= 1 << k; + } + else { + k = -k; + word0(db) += (k >> 2)*Exp_msk1; + if (k &= 3) + db *= 1 << k; + } +#else + if (k > 0) + word0(da) += k*Exp_msk1; + else { + k = -k; + word0(db) += k*Exp_msk1; + } +#endif + return da / db; +} + +static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif +}; + +static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +void _PR_InitDtoa(void) +{ + freelist_lock = PR_NewLock(); + p5s_lock = PR_NewLock(); +} + +#if defined(HAVE_WATCOM_BUG_1) +PRFloat64 __pascal __loadds __export +#else +PR_IMPLEMENT(PRFloat64) +#endif +PR_strtod(CONST char *s00, char **se) +{ + PRInt32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, + e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + PRFloat64 aadj, aadj1, adj, rv, rv0; + Long L; + unsigned Long y, z; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; + + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + sign = nz0 = nz = 0; + rv = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + s = s00; + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } +break2: + if (*s == '0') { + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + if (c == '.') { + c = *++s; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } +dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (PRInt32)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) + s = s00; + goto ret; + } + e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + rv = y; + if (k > 9) + rv = tens[k - 9] * rv + z; + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT + && FLT_ROUNDS == 1 +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else + /* rv = */ rounded_product(rv, tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e -= i; + rv *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(rv) -= P*Exp_msk1; + /* rv = */ rounded_product(rv, tens[e]); + if ((word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(rv, tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(rv, tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15) != 0) + rv *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: + errno = ERANGE; +#ifdef __STDC__ + rv = HUGE_VAL; +#else + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith + word0(rv) = Exp_mask; + word1(rv) = 0; +#else + word0(rv) = Big0; + word1(rv) = Big1; +#endif +#endif + if (bd0) + goto retfree; + goto ret; + } + if (e1 >>= 4) { + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(rv) -= P*Exp_msk1; + rv *= bigtens[j]; + if ((z = word0(rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(rv) = Big0; + word1(rv) = Big1; + } + else + word0(rv) += P*Exp_msk1; + } + + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15) != 0) + rv /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + if (e1 >= 1 << n_bigtens) + goto undfl; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + rv *= tinytens[j]; + /* The last multiplication could underflow. */ + rv0 = rv; + rv *= tinytens[j]; + if (!rv) { + rv = 2.*rv0; + rv *= tinytens[j]; + if (!rv) { + undfl: + rv = 0.; + errno = ERANGE; + if (bd0) + goto retfree; + goto ret; + } + word0(rv) = Tiny0; + word1(rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else + i = bbe + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j = bbe + (P-Emin); + else + j = P + 1 - bbbits; +#endif + bb2 += j; + bd2 += j; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) + break; + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if ((word0(rv) & Bndry_mask1) == Bndry_mask1 + && word1(rv) == 0xffffffff) { + /*boundary case -- increment exponent*/ + word0(rv) = (word0(rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(rv) = 0; + break; + } + } + else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow + L = word0(rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else + if (L <= Exp_msk1) +#endif + goto undfl; + L -= Exp_msk1; +#else + L = (long)((word0(rv) & Exp_mask) - Exp_msk1); +#endif + word0(rv) = L | Bndry_mask1; + word1(rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(rv) & LSB)) + break; +#endif + if (dsign) + rv += ulp(rv); +#ifndef ROUND_BIASED + else { + rv -= ulp(rv); +#ifndef Sudden_Underflow + if (!rv) + goto undfl; +#endif + } +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (dsign) + aadj = aadj1 = 1.; + else if (word1(rv) || word0(rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(rv) == Tiny1 && !word0(rv)) + goto undfl; +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(FLT_ROUNDS) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (FLT_ROUNDS == 0) + aadj1 += 0.5; +#endif + } + y = word0(rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + rv0 = rv; + word0(rv) -= P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; + if ((word0(rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(rv0) == Big0 && word1(rv0) == Big1) + goto ovfl; + word0(rv) = Big0; + word1(rv) = Big1; + goto cont; + } + else + word0(rv) += P*Exp_msk1; + } + else { +#ifdef Sudden_Underflow + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { + rv0 = rv; + word0(rv) += P*Exp_msk1; + adj = aadj1 * ulp(rv); + rv += adj; +#ifdef IBM + if ((word0(rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(rv0) == Tiny0 + && word1(rv0) == Tiny1) + goto undfl; + word0(rv) = Tiny0; + word1(rv) = Tiny1; + goto cont; + } + else + word0(rv) -= P*Exp_msk1; + } + else { + adj = aadj1 * ulp(rv); + rv += adj; + } +#else + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj >= 1.) { + aadj1 = (double)(PRInt32)(aadj + 0.5); + if (!dsign) + aadj1 = -aadj1; + } + adj = aadj1 * ulp(rv); + rv += adj; +#endif + } + z = word0(rv) & Exp_mask; + if (y == z) { + /* Can we stop now? */ + L = (Long) aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (dsign || word1(rv) || word0(rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } +retfree: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +ret: + if (se) + *se = (char *)s; + return sign ? -rv : rv; +} + +static PRInt32 +quorem(Bigint *b, Bigint *S) +{ + PRInt32 n; + Long borrow, y; + unsigned Long carry, q, ys; + unsigned Long *bx, *bxe, *sx, *sxe; +#ifdef Pack_32 + Long z; + unsigned Long si, zs; +#endif + + n = S->wds; +#ifdef DEBUG_DTOA + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG_DTOA + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (long)((*bx & 0xffff) - (ys & 0xffff) + borrow); + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (long)((*bx >> 16) - (zs & 0xffff) + borrow); + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (long)((*bx & 0xffff) - (ys & 0xffff) + borrow); + borrow = y >> 16; + Sign_Extend(borrow, y); + z = (long)((*bx >> 16) - (zs & 0xffff) + borrow); + borrow = z >> 16; + Sign_Extend(borrow, z); + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) + borrow; + borrow = y >> 16; + Sign_Extend(borrow, y); + *bx++ = y & 0xffff; +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return (int)q; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +PR_IMPLEMENT(char *) +PR_dtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + PRInt32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + PRInt32 denorm; + unsigned Long x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s, *s0; + static Bigint *result; + static PRInt32 result_k; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (result) { + result->k = result_k; + result->maxwds = 1 << result_k; + Bfree(result); + result = 0; + } + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; + s = +#ifdef IEEE_Arith + !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" : +#endif + "NaN"; + if (rve) + *rve = +#ifdef IEEE_Arith + s[3] ? s + 8 : +#endif + s + 3; + return s; + } +#endif +#ifdef IBM + d += 0; /* normalize */ +#endif + if (!d) { + *decpt = 1; + s = "0"; + if (rve) + *rve = s + 1; + return s; + } + + b = d2b(d, &be, &bbits); +#ifdef Sudden_Underflow + i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { +#endif + d2 = d; + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + d2 /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) + : word1(d) << (32 - i); + d2 = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (PRInt32)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + j = sizeof(unsigned Long); + for(result_k = 0; sizeof(Bigint) - sizeof(unsigned Long) <= i - j; + j <<= 1) result_k++; + result = Balloc(result_k); + s = s0 = (char *)result; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if ((j1 = -k) != 0) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + eps = ieps*d + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (Long) d; + d -= L; + *s++ = '0' + (PRInt32)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.) { + L = (Long) d; + d -= L; + *s++ = '0' + (PRInt32)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while(*--s == '0'){} /* just count -- nothing to execute */ + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + d = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || d <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = (Long) (d / ds); + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (PRInt32)L; + if (i == ilim) { + d += d; + if ((d > ds) || (d == ds && L & 1)) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5) != 0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & Exp_mask +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + else + spec_case = 0; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } +#endif + if ((j < 0) || ((j == 0) && (!mode) +#ifndef ROUND_BIASED + && (!(word1(d) & 1))) +#endif + ) { + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if (((j1 > 0) || (j1 == 0 && dig & 1)) + && (dig++ == '9')) + goto round_9_up; + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + b = lshift(b, 1); + j = cmp(b, S); + if ((j > 0) || (j == 0 && dig & 1)) { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + while(*--s == '0'){} /* just count -- nothing to execute */ + s++; + } +ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } +ret1: + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +PR_IMPLEMENT(PRStatus) +PR_dtoa_r(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve, char *buf, PRSize bufsize) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + PRInt32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + PRInt32 denorm; + unsigned Long x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + double d2, ds, eps; + char *s, *s0; + Bigint *result = 0; + PRInt32 result_k; + PRStatus retval; + PRSize strsize; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (word0(d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(d) & Exp_mask) == Exp_mask) +#else + if (word0(d) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; + s = +#ifdef IEEE_Arith + !word1(d) && !(word0(d) & 0xfffff) ? "Infinity" : +#endif + "NaN"; + if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return PR_FAILURE; + } + strcpy(buf, s); + if (rve) { + *rve = +#ifdef IEEE_Arith + buf[3] ? buf + 8 : +#endif + buf + 3; + PR_ASSERT(**rve == '\0'); + } + return PR_SUCCESS; + } +#endif +#ifdef IBM + d += 0; /* normalize */ +#endif + if (!d) { + *decpt = 1; + if (bufsize < 2) { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return PR_FAILURE; + } + buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ + if (rve) { + *rve = buf + 1; + PR_ASSERT(**rve == '\0'); + } + return PR_SUCCESS; + } + + b = d2b(d, &be, &bbits); +#ifdef Sudden_Underflow + i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (PRInt32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { +#endif + d2 = d; + word0(d2) &= Frac_mask1; + word0(d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(d2) & Frac_mask)) + d2 /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) + : word1(d) << (32 - i); + d2 = x; + word0(d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (PRInt32)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (d < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + switch(mode) { + case 0: + case 1: + ilim = ilim1 = -1; + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + j = sizeof(unsigned Long); + for(result_k = 0; sizeof(Bigint) - sizeof(unsigned Long) <= i - j; + j <<= 1) result_k++; + result = Balloc(result_k); + s = s0 = (char *)result; + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = d; + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + d /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + d /= ds; + } + else if ((j1 = -k) != 0) { + d *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + d *= bigtens[i]; + } + } + if (k_check && d < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + d *= 10.; + ieps++; + } + eps = ieps*d + 7.; + word0(eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + d -= 5.; + if (d > eps) + goto one_digit; + if (d < -eps) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + eps = 0.5/tens[ilim-1] - eps; + for(i = 0;;) { + L = (Long) d; + d -= L; + *s++ = '0' + (PRInt32)L; + if (d < eps) + goto ret1; + if (1. - d < eps) + goto bump_up; + if (++i >= ilim) + break; + eps *= 10.; + d *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + eps *= tens[ilim-1]; + for(i = 1;; i++, d *= 10.) { + L = (Long) d; + d -= L; + *s++ = '0' + (PRInt32)L; + if (i == ilim) { + if (d > 0.5 + eps) + goto bump_up; + else if (d < 0.5 - eps) { + while(*--s == '0'){} /* just count -- nothing to execute */ + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + d = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || d <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++) { + L = (Long) (d / ds); + d -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (d < 0) { + L--; + d += ds; + } +#endif + *s++ = '0' + (PRInt32)L; + if (i == ilim) { + d += d; + if ((d > ds) || (d == ds && L & 1)) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + if (!(d *= 10.)) + break; + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + if (mode < 2) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + } + else { + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5) != 0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + if (mode < 2) { + if (!word1(d) && !(word0(d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(d) & Exp_mask +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + else + spec_case = 0; + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) + i = 32 - i; +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && !mode && !(word1(d) & 1)) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; + *s++ = dig; + goto ret; + } +#endif + if ((j < 0) || ((j == 0) && (!mode) +#ifndef ROUND_BIASED + && (!(word1(d) & 1))) +#endif + ) { + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if (((j1 > 0) || (j1 == 0 && dig & 1)) + && (dig++ == '9')) + goto round_9_up; + } + *s++ = dig; + goto ret; + } + if (j1 > 0) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + b = lshift(b, 1); + j = cmp(b, S); + if ((j > 0) || (j == 0 && dig & 1)) { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + while(*--s == '0'){} /* just count -- nothing to execute */ + s++; + } +ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } +ret1: + Bfree(b); + *s = 0; + *decpt = k + 1; + strsize = (s - s0) + 1; + if (strsize <= bufsize) { + retval = PR_SUCCESS; + memcpy(buf, s0, strsize); + if (rve) { + *rve = buf + strsize - 1; + PR_ASSERT(**rve == '\0'); + } + } else { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + retval = PR_FAILURE; + } + + /* cleanup */ + result->k = result_k; + result->maxwds = 1 << result_k; + Bfree(result); + + return retval; +} + +/* +** conversion routines for floating point +** prcsn - number of digits of precision to generate floating +** point value. +** This should be reparameterized so that you can send in a +** prcn for the positive and negative ranges. For now, +** conform to the ECMA JavaScript spec which says numbers +** less than 1e-6 are in scientific notation. +** Also, the ECMA spec says that there should always be a +** '+' or '-' after the 'e' in scientific notation +*/ +PR_IMPLEMENT(void) +PR_cnvtf(char *buf,int bufsz, int prcsn,double fval) +{ + PRIntn decpt, sign, numdigits; + char *num, *nump; + char *bufp = buf; + char *endnum; + + /* If anything fails, we store an empty string in 'buf' */ + num = (char*)PR_MALLOC(bufsz); + if (num == NULL) { + buf[0] = '\0'; + return; + } + /* XXX Why use mode 1? */ + if (PR_dtoa_r(fval,1,prcsn,&decpt,&sign,&endnum,num,bufsz) + == PR_FAILURE) { + buf[0] = '\0'; + goto done; + } + numdigits = endnum - num; + nump = num; + + if (sign && + !(word0(fval) == Sign_bit && word1(fval) == 0) && + !((word0(fval) & Exp_mask) == Exp_mask && + (word1(fval) || (word0(fval) & 0xfffff)))) { + *bufp++ = '-'; + } + + if(decpt == 9999){ + while((*bufp++ = *nump++) != 0){} /* nothing to execute */ + goto done; + } + + if(decpt > (prcsn+1) || decpt < -(prcsn-1) || decpt < -5){ + *bufp++ = *nump++; + if(numdigits != 1){ + *bufp++ = '.'; + } + + while(*nump != '\0'){ + *bufp++ = *nump++; + } + *bufp++ = 'e'; + PR_snprintf(bufp,bufsz - (bufp - buf), "%+d",decpt-1); + } + else if(decpt >= 0){ + while(decpt--){ + if(*nump != '\0'){ + *bufp++ = *nump++; + } + else { + *bufp++ = '0'; + } + } + if(*nump != '\0'){ + *bufp++ = '.'; + while(*nump != '\0'){ + *bufp++ = *nump++; + } + } + *bufp++ = '\0'; + } + else if(decpt < 0){ + *bufp++ = '0'; + *bufp++ = '.'; + while(decpt++){ + *bufp++ = '0'; + } + + while(*nump != '\0'){ + *bufp++ = *nump++; + } + *bufp++ = '\0'; + } +done: + PR_DELETE(num); +} diff --git a/pr/src/misc/prenv.c b/pr/src/misc/prenv.c new file mode 100644 index 00000000..b6b36fad --- /dev/null +++ b/pr/src/misc/prenv.c @@ -0,0 +1,55 @@ +/* -*- 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 <string.h> +#include "primpl.h" + +/* Lock used to lock the environment */ +#if defined(_PR_NO_PREEMPT) +#define _PR_NEW_LOCK_ENV() +#define _PR_LOCK_ENV() +#define _PR_UNLOCK_ENV() +#elif defined(_PR_LOCAL_THREADS_ONLY) +#define _PR_NEW_LOCK_ENV() +#define _PR_LOCK_ENV() { PRIntn _is; _PR_INTSOFF(_is) +#define _PR_UNLOCK_ENV() _PR_INTSON(_is); } +#else +static PRLock *_pr_envLock; +#define _PR_NEW_LOCK_ENV() {_pr_envLock = PR_NewLock();} +#define _PR_LOCK_ENV() PR_Lock(_pr_envLock) +#define _PR_UNLOCK_ENV() PR_Unlock(_pr_envLock) +#endif + +/************************************************************************/ + +void _PR_InitEnv() +{ + _PR_NEW_LOCK_ENV(); +} + +PR_IMPLEMENT(char*) PR_GetEnv(const char *var) +{ + char *ev; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + _PR_LOCK_ENV(); + ev = _PR_MD_GET_ENV(var); + _PR_UNLOCK_ENV(); + return ev; +} diff --git a/pr/src/misc/prerror.c b/pr/src/misc/prerror.c new file mode 100644 index 00000000..c7ea4707 --- /dev/null +++ b/pr/src/misc/prerror.c @@ -0,0 +1,89 @@ +/* -*- 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 <memory.h> +#include <string.h> +#include <stdlib.h> + +PR_IMPLEMENT(PRErrorCode) PR_GetError() +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->errorCode; +} + +PR_IMPLEMENT(PRInt32) PR_GetOSError() +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->osErrorCode; +} + +PR_IMPLEMENT(const char*) PR_GetErrorString() +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->errorString; +} + +PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr) +{ + PRThread *thread = PR_GetCurrentThread(); + thread->errorCode = code; + thread->osErrorCode = osErr; + thread->errorStringSize = 0; + PR_DELETE(thread->errorString); +} + +PR_IMPLEMENT(void) PR_SetErrorText(PRIntn textLength, const char *text) +{ + PRThread *thread = PR_GetCurrentThread(); + + if (0 == textLength) + { + if (NULL != thread->errorString) + PR_DELETE(thread->errorString); + } + else + { + PRIntn size = textLength + 1; /* actual length to allocate */ + if (thread->errorStringSize < textLength) /* do we have room? */ + { + if (NULL != thread->errorString) + PR_DELETE(thread->errorString); + thread->errorString = (char*)PR_MALLOC(size); + } + memcpy(thread->errorString, text, size); + } + thread->errorStringSize = textLength; +} + +PR_IMPLEMENT(PRInt32) PR_GetErrorTextLength(void) +{ + PRThread *thread = PR_GetCurrentThread(); + return thread->errorStringSize; +} /* PR_GetErrorTextLength */ + +PR_IMPLEMENT(PRInt32) PR_GetErrorText(char *text) +{ + PRThread *thread = PR_GetCurrentThread(); + if (0 != thread->errorStringSize) + memcpy(text, thread->errorString, thread->errorStringSize + 1); + return thread->errorStringSize; +} /* PR_GetErrorText */ + + diff --git a/pr/src/misc/prinit.c b/pr/src/misc/prinit.c new file mode 100644 index 00000000..247aadb9 --- /dev/null +++ b/pr/src/misc/prinit.c @@ -0,0 +1,519 @@ +/* -*- 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 <string.h> + +PRLogModuleInfo *_pr_clock_lm; +PRLogModuleInfo *_pr_cmon_lm; +PRLogModuleInfo *_pr_io_lm; +PRLogModuleInfo *_pr_cvar_lm; +PRLogModuleInfo *_pr_mon_lm; +PRLogModuleInfo *_pr_linker_lm; +PRLogModuleInfo *_pr_sched_lm; +PRLogModuleInfo *_pr_thread_lm; +PRLogModuleInfo *_pr_gc_lm; + +PRFileDesc *_pr_stdin; +PRFileDesc *_pr_stdout; +PRFileDesc *_pr_stderr; + +#if !defined(_PR_PTHREADS) + +PRCList _pr_active_local_threadQ = + PR_INIT_STATIC_CLIST(&_pr_active_local_threadQ); +PRCList _pr_active_global_threadQ = + PR_INIT_STATIC_CLIST(&_pr_active_global_threadQ); + +_MDLock _pr_cpuLock; /* lock for the CPU Q */ +PRCList _pr_cpuQ = PR_INIT_STATIC_CLIST(&_pr_cpuQ); + +PRUint32 _pr_utid; + +PRUintn _pr_userActive; +PRUintn _pr_systemActive; +PRUintn _pr_maxPTDs; + +#ifdef _PR_LOCAL_THREADS_ONLY + +struct _PRCPU *_pr_currentCPU; +PRThread *_pr_currentThread; +PRThread *_pr_lastThread; +PRInt32 _pr_intsOff; + +#endif /* _PR_LOCAL_THREADS_ONLY */ + +/* Lock protecting all "termination" condition variables of all threads */ +PRLock *_pr_terminationCVLock; + +#endif /* !defined(_PR_PTHREADS) */ + +static void _PR_InitCallOnce(void); +static void _PR_InitStuff(void); + +/************************************************************************/ +/**************************IDENTITY AND VERSIONING***********************/ +/************************************************************************/ +PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion) +{ + /* + ** This is the secret handshake algorithm. Right now it requires + ** an exact match. Later it should get more tricky. + */ + return ((0 == strcmp(importedVersion, PR_VERSION)) ? PR_TRUE : PR_FALSE); +} /* PR_VersionCheck */ + + +PRBool _pr_initialized = PR_FALSE; + +PR_IMPLEMENT(PRBool) PR_Initialized(void) +{ + return _pr_initialized; +} + +static void _PR_InitStuff() +{ + if (_pr_initialized) return; + _pr_initialized = PR_TRUE; + + (void) PR_GetPageSize(); + + _pr_clock_lm = PR_NewLogModule("clock"); + _pr_cmon_lm = PR_NewLogModule("cmon"); + _pr_io_lm = PR_NewLogModule("io"); + _pr_mon_lm = PR_NewLogModule("mon"); + _pr_linker_lm = PR_NewLogModule("linker"); + _pr_cvar_lm = PR_NewLogModule("cvar"); + _pr_sched_lm = PR_NewLogModule("sched"); + _pr_thread_lm = PR_NewLogModule("thread"); + _pr_gc_lm = PR_NewLogModule("gc"); + + /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */ + _PR_MD_EARLY_INIT(); + + _PR_InitAtomic(); + _PR_InitLocks(); + _PR_InitSegs(); + _PR_InitStacks(); + _PR_InitTPD(); + _PR_InitEnv(); + _PR_InitLayerCache(); + + _PR_InitThreads(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + +#ifdef WIN16 + { + PRInt32 top; /* artificial top of stack, win16 */ + _pr_top_of_task_stack = (char *) ⊤ + } +#endif + +#ifndef _PR_GLOBAL_THREADS_ONLY + _PR_InitCPUs(); +#endif + +/* + * XXX: call _PR_InitMem only on those platforms for which nspr implements + * malloc, for now. + */ +#ifdef _PR_OVERRIDE_MALLOC + _PR_InitMem(); +#endif + + _PR_InitCMon(); + _PR_InitIO(); + _PR_InitNet(); + _PR_InitClock(); +#ifdef PR_LOGGING + _PR_InitLog(); +#endif + _PR_InitLinker(); + _PR_InitCallOnce(); + _PR_InitDtoa(); + + _PR_MD_FINAL_INIT(); +} + +void _PR_ImplicitInitialization() +{ + _PR_InitStuff(); + + /* Enable interrupts */ +#ifndef _PR_GLOBAL_THREADS_ONLY + _PR_MD_START_INTERRUPTS(); +#endif + +} + +PR_IMPLEMENT(void) PR_DisableClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) + if (!_pr_initialized) { + _PR_InitStuff(); + } else { + _PR_MD_DISABLE_CLOCK_INTERRUPTS(); + } +#endif +} + +PR_IMPLEMENT(void) PR_BlockClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) + _PR_MD_BLOCK_CLOCK_INTERRUPTS(); +#endif +} + +PR_IMPLEMENT(void) PR_UnblockClockInterrupts(void) +{ +#if !defined(_PR_PTHREADS) + _PR_MD_UNBLOCK_CLOCK_INTERRUPTS(); +#endif +} + +PR_IMPLEMENT(void) PR_Init( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) +{ +#if defined(XP_MAC) +#pragma unused (type, priority, maxPTDs) +#endif + + _PR_ImplicitInitialization(); +} + +PR_IMPLEMENT(PRIntn) PR_Initialize( + PRPrimordialFn prmain, PRIntn argc, char **argv, PRUintn maxPTDs) +{ +#if defined(XP_MAC) +#pragma unused (maxPTDs) +#endif + + PRIntn rv; + _PR_ImplicitInitialization(); + rv = prmain(argc, argv); + PR_Cleanup(); + return rv; +} /* PR_Initialize */ + +/* + *----------------------------------------------------------------------- + * + * _PR_CleanupBeforeExit -- + * + * Perform the cleanup work before exiting the process. + * We first do the cleanup generic to all platforms. Then + * we call _PR_MD_CLEANUP_BEFORE_EXIT(), where platform-dependent + * cleanup is done. This function is used by PR_Cleanup(). + * + * See also: PR_Cleanup(). + * + *----------------------------------------------------------------------- + */ +#if defined(_PR_PTHREADS) + /* see ptthread.c */ +#else +static void +_PR_CleanupBeforeExit(void) +{ +/* +Do not make any calls here other than to destroy resources. For example, +do not make any calls that eventually may end up in PR_Lock. Because the +thread is destroyed, can not access current thread any more. +*/ + _PR_CleanupTPD(); + if (_pr_terminationCVLock) + /* + * In light of the comment above, this looks real suspicious. + * I'd go so far as to say it's just a problem waiting to happen. + */ + PR_DestroyLock(_pr_terminationCVLock); + + _PR_MD_CLEANUP_BEFORE_EXIT(); +} +#endif /* defined(_PR_PTHREADS) */ + +/* + *---------------------------------------------------------------------- + * + * PR_Cleanup -- + * + * Perform a graceful shutdown of the NSPR runtime. PR_Cleanup() may + * only be called from the primordial thread, typically at the + * end of the main() function. It returns when it has completed + * its platform-dependent duty and the process must not make any other + * NSPR library calls prior to exiting from main(). + * + * PR_Cleanup() first blocks the primordial thread until all the + * other user (non-system) threads, if any, have terminated. + * Then it performs cleanup in preparation for exiting the process. + * PR_Cleanup() does not exit the primordial thread (which would + * in turn exit the process). + * + * PR_Cleanup() only responds when it is called by the primordial + * thread. Calls by any other thread are silently ignored. + * + * See also: PR_ExitProcess() + * + *---------------------------------------------------------------------- + */ +#if defined(_PR_PTHREADS) + /* see ptthread.c */ +#else + +PR_IMPLEMENT(PRStatus) PR_Cleanup() +{ + PRThread *me = PR_GetCurrentThread(); + PR_ASSERT((NULL != me) && (me->flags & _PR_PRIMORDIAL)); + if ((NULL != me) && (me->flags & _PR_PRIMORDIAL)) + { + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); + + /* + * No more recycling of threads + */ + _pr_recycleThreads = 0; + + /* + * Wait for all other user (non-system/daemon) threads + * to terminate. + */ + PR_Lock(_pr_activeLock); + while (_pr_userActive > _pr_primordialExitCount) { + PR_WaitCondVar(_pr_primordialExitCVar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(_pr_activeLock); + +#if defined(WIN16) + _PR_ShutdownLinker(); +#endif + /* Release the primordial thread's private data, etc. */ + _PR_CleanupThread(me); + + _PR_MD_STOP_INTERRUPTS(); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, + ("PR_Cleanup: clean up before destroying thread")); + #ifdef PR_LOGGING + _PR_LogCleanup(); + #endif + + /* + * This part should look like the end of _PR_NativeRunThread + * and _PR_UserRunThread. + */ + if (_PR_IS_NATIVE_THREAD(me)) { + _PR_MD_EXIT_THREAD(me); + _PR_NativeDestroyThread(me); + } else { + _PR_UserDestroyThread(me); + PR_DELETE(me->stack); + PR_DELETE(me); + } + + /* + * XXX: We are freeing the heap memory here so that Purify won't + * complain, but we should also free other kinds of resources + * that are allocated by the _PR_InitXXX() functions. + * Ideally, for each _PR_InitXXX(), there should be a corresponding + * _PR_XXXCleanup() that we can call here. + */ + _PR_CleanupBeforeExit(); + return PR_SUCCESS; + } + return PR_FAILURE; +} +#endif /* defined(_PR_PTHREADS) */ + +/* + *------------------------------------------------------------------------ + * PR_ProcessExit -- + * + * Cause an immediate, nongraceful, forced termination of the process. + * It takes a PRIntn argument, which is the exit status code of the + * process. + * + * See also: PR_Cleanup() + * + *------------------------------------------------------------------------ + */ + +#if defined(_PR_PTHREADS) + /* see ptthread.c */ +#else +PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) +{ + _PR_MD_EXIT(status); +} + +#endif /* defined(_PR_PTHREADS) */ + +PR_IMPLEMENT(PRProcessAttr *) +PR_NewProcessAttr(void) +{ + PRProcessAttr *attr; + + attr = PR_NEWZAP(PRProcessAttr); + if (!attr) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return attr; +} + +PR_IMPLEMENT(void) +PR_ResetProcessAttr(PRProcessAttr *attr) +{ + memset(attr, 0, sizeof(*attr)); +} + +PR_IMPLEMENT(void) +PR_DestroyProcessAttr(PRProcessAttr *attr) +{ + PR_DELETE(attr); +} + +PR_IMPLEMENT(void) +PR_SetStdioRedirect( + PRProcessAttr *attr, + PRSpecialFD stdioFd, + PRFileDesc *redirectFd +) +{ + switch (stdioFd) { + case PR_StandardInput: + attr->stdinFd = redirectFd; + break; + case PR_StandardOutput: + attr->stdoutFd = redirectFd; + break; + case PR_StandardError: + attr->stderrFd = redirectFd; + break; + default: + PR_ASSERT(0); + } +} + +PR_IMPLEMENT(PRProcess*) PR_CreateProcess( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + return _PR_MD_CREATE_PROCESS(path, argv, envp, attr); +} /* PR_CreateProcess */ + +PR_IMPLEMENT(PRStatus) PR_CreateProcessDetached( + const char *path, + char *const *argv, + char *const *envp, + const PRProcessAttr *attr) +{ + PRProcess *process; + PRStatus rv; + + process = PR_CreateProcess(path, argv, envp, attr); + if (NULL == process) { + return PR_FAILURE; + } + rv = PR_DetachProcess(process); + PR_ASSERT(PR_SUCCESS == rv); + if (rv == PR_FAILURE) { + PR_DELETE(process); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_DetachProcess(PRProcess *process) +{ + return _PR_MD_DETACH_PROCESS(process); +} + +PR_IMPLEMENT(PRStatus) PR_WaitProcess(PRProcess *process, PRInt32 *exitCode) +{ + return _PR_MD_WAIT_PROCESS(process, exitCode); +} /* PR_WaitProcess */ + +PR_IMPLEMENT(PRStatus) PR_KillProcess(PRProcess *process) +{ + return _PR_MD_KILL_PROCESS(process); +} + +/* + ******************************************************************** + * + * Module initialization + * + ******************************************************************** + */ + +static struct { + PRLock *ml; + PRCondVar *cv; +} mod_init; + +static void _PR_InitCallOnce() { + mod_init.ml = PR_NewLock(); + PR_ASSERT(NULL != mod_init.ml); + mod_init.cv = PR_NewCondVar(mod_init.ml); + PR_ASSERT(NULL != mod_init.cv); +} + + +PR_IMPLEMENT(PRStatus) PR_CallOnce( + PRCallOnceType *once, + PRCallOnceFN func) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (!once->initialized) { + if (PR_AtomicSet(&once->inProgress, 1) == 0) { + once->status = (*func)(); + PR_Lock(mod_init.ml); + once->initialized = 1; + PR_NotifyAllCondVar(mod_init.cv); + PR_Unlock(mod_init.ml); + } else { + PR_Lock(mod_init.ml); + while (!once->initialized) { + PR_WaitCondVar(mod_init.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(mod_init.ml); + } + } + return once->status; +} + +PRBool _PR_Obsolete(const char *obsolete, const char *preferred) +{ +#if defined(DEBUG) +#ifndef XP_MAC + PR_fprintf( + PR_STDERR, "'%s' is obsolete. Use '%s' instead.\n", + obsolete, (NULL == preferred) ? "something else" : preferred); + return PR_FALSE; +#else +#pragma unused (obsolete, preferred) + return PR_FALSE; +#endif +#endif +} /* _PR_Obsolete */ + +/* prinit.c */ + + diff --git a/pr/src/misc/prinrval.c b/pr/src/misc/prinrval.c new file mode 100644 index 00000000..2fe46a76 --- /dev/null +++ b/pr/src/misc/prinrval.c @@ -0,0 +1,130 @@ +/* -*- 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. + */ + +/* + * file: prinrval.c + * description: implementation for the kernel interval timing functions + */ + +#include "primpl.h" + +/* + *----------------------------------------------------------------------- + * + * _PR_InitClock -- + * + * + *----------------------------------------------------------------------- + */ + +void _PR_InitClock(void) +{ + _PR_MD_INTERVAL_INIT(); +} + +/* + * This version of interval times is based on the time of day + * capability offered by system. This isn't valid for two reasons: + * 1) The time of day is neither linear nor montonically increasing + * 2) The units here are milliseconds. That's not appropriate for our use. + */ + +PR_IMPLEMENT(PRIntervalTime) PR_IntervalNow() +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return _PR_MD_GET_INTERVAL(); +} /* PR_IntervalNow */ + +PR_EXTERN(PRUint32) PR_TicksPerSecond() +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return _PR_MD_INTERVAL_PER_SEC(); +} /* PR_TicksPerSecond */ + +PR_IMPLEMENT(PRIntervalTime) PR_SecondsToInterval(PRUint32 seconds) +{ + return seconds * PR_TicksPerSecond(); +} /* PR_SecondsToInterval */ + +PR_IMPLEMENT(PRIntervalTime) PR_MillisecondsToInterval(PRUint32 milli) +{ + PRIntervalTime ticks; + PRUint64 tock, tps, msecPerSec, rounding; + LL_UI2L(tock, milli); + LL_I2L(msecPerSec, PR_MSEC_PER_SEC); + LL_I2L(rounding, (PR_MSEC_PER_SEC >> 1)); + LL_I2L(tps, PR_TicksPerSecond()); + LL_MUL(tock, tock, tps); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, msecPerSec); + LL_L2UI(ticks, tock); + return ticks; +} /* PR_MillisecondsToInterval */ + +PR_IMPLEMENT(PRIntervalTime) PR_MicrosecondsToInterval(PRUint32 micro) +{ + PRIntervalTime ticks; + PRUint64 tock, tps, usecPerSec, rounding; + LL_UI2L(tock, micro); + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(rounding, (PR_USEC_PER_SEC >> 1)); + LL_I2L(tps, PR_TicksPerSecond()); + LL_MUL(tock, tock, tps); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, usecPerSec); + LL_L2UI(ticks, tock); + return ticks; +} /* PR_MicrosecondsToInterval */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToSeconds(PRIntervalTime ticks) +{ + return ticks / PR_TicksPerSecond(); +} /* PR_IntervalToSeconds */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToMilliseconds(PRIntervalTime ticks) +{ + PRUint32 milli; + PRUint64 tock, tps, msecPerSec, rounding; + LL_UI2L(tock, ticks); + LL_I2L(msecPerSec, PR_MSEC_PER_SEC); + LL_I2L(tps, PR_TicksPerSecond()); + LL_USHR(rounding, tps, 1); + LL_MUL(tock, tock, msecPerSec); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, tps); + LL_L2UI(milli, tock); + return milli; +} /* PR_IntervalToMilliseconds */ + +PR_IMPLEMENT(PRUint32) PR_IntervalToMicroseconds(PRIntervalTime ticks) +{ + PRUint32 micro; + PRUint64 tock, tps, usecPerSec, rounding; + LL_UI2L(tock, ticks); + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_I2L(tps, PR_TicksPerSecond()); + LL_USHR(rounding, tps, 1); + LL_MUL(tock, tock, usecPerSec); + LL_ADD(tock, tock, rounding); + LL_DIV(tock, tock, tps); + LL_L2UI(micro, tock); + return micro; +} /* PR_IntervalToMicroseconds */ + +/* prinrval.c */ + diff --git a/pr/src/misc/prlog2.c b/pr/src/misc/prlog2.c new file mode 100644 index 00000000..e733c7b3 --- /dev/null +++ b/pr/src/misc/prlog2.c @@ -0,0 +1,62 @@ +/* -*- 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 "prbit.h" + +/* +** Compute the log of the least power of 2 greater than or equal to n +*/ +PR_IMPLEMENT(PRIntn) PR_CeilingLog2(PRUint32 n) +{ + PRIntn log2 = 0; + + if (n & (n-1)) + log2++; + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; + return log2; +} + +/* +** Compute the log of the greatest power of 2 less than or equal to n. +** This really just finds the highest set bit in the word. +*/ +PR_IMPLEMENT(PRIntn) PR_FloorLog2(PRUint32 n) +{ + PRIntn log2 = 0; + + if (n >> 16) + log2 += 16, n >>= 16; + if (n >> 8) + log2 += 8, n >>= 8; + if (n >> 4) + log2 += 4, n >>= 4; + if (n >> 2) + log2 += 2, n >>= 2; + if (n >> 1) + log2++; + return log2; +} diff --git a/pr/src/misc/prlong.c b/pr/src/misc/prlong.c new file mode 100644 index 00000000..85e200c1 --- /dev/null +++ b/pr/src/misc/prlong.c @@ -0,0 +1,259 @@ +/* -*- 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 PRInt64 ll_zero = LL_INIT( 0x00000000,0x00000000 ); +static PRInt64 ll_maxint = LL_INIT( 0x7fffffff, 0xffffffff ); +static PRInt64 ll_minint = LL_INIT( 0x80000000, 0x00000000 ); + +#if defined(HAVE_WATCOM_BUG_2) +PRInt64 __pascal __loadds __export + LL_Zero(void) { return ll_zero; } +PRInt64 __pascal __loadds __export + LL_MaxInt(void) { return ll_maxint; } +PRInt64 __pascal __loadds __export + LL_MinInt(void) { return ll_minint; } +#else +PR_IMPLEMENT(PRInt64) LL_Zero(void) { return ll_zero; } +PR_IMPLEMENT(PRInt64) LL_MaxInt(void) { return ll_maxint; } +PR_IMPLEMENT(PRInt64) LL_MinInt(void) { return ll_minint; } +#endif + +#ifndef HAVE_LONG_LONG +/* +** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. +*/ +static void norm_udivmod32(PRUint32 *qp, PRUint32 *rp, PRUint64 a, PRUint32 b) +{ + PRUint32 d1, d0, q1, q0; + PRUint32 r1, r0, m; + + d1 = _hi16(b); + d0 = _lo16(b); + r1 = a.hi % d1; + q1 = a.hi / d1; + m = q1 * d0; + r1 = (r1 << 16) | _hi16(a.lo); + if (r1 < m) { + q1--, r1 += b; + if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ + && r1 < m) { + q1--, r1 += b; + } + } + r1 -= m; + r0 = r1 % d1; + q0 = r1 / d1; + m = q0 * d0; + r0 = (r0 << 16) | _lo16(a.lo); + if (r0 < m) { + q0--, r0 += b; + if (r0 >= b + && r0 < m) { + q0--, r0 += b; + } + } + *qp = (q1 << 16) | q0; + *rp = r0 - m; +} + +static PRUint32 CountLeadingZeros(PRUint32 a) +{ + PRUint32 t; + PRUint32 r = 32; + + if ((t = a >> 16) != 0) + r -= 16, a = t; + if ((t = a >> 8) != 0) + r -= 8, a = t; + if ((t = a >> 4) != 0) + r -= 4, a = t; + if ((t = a >> 2) != 0) + r -= 2, a = t; + if ((t = a >> 1) != 0) + r -= 1, a = t; + if (a & 1) + r--; + return r; +} + +PR_IMPLEMENT(void) ll_udivmod(PRUint64 *qp, PRUint64 *rp, PRUint64 a, PRUint64 b) +{ + PRUint32 n0, n1, n2; + PRUint32 q0, q1; + PRUint32 rsh, lsh; + + n0 = a.lo; + n1 = a.hi; + + if (b.hi == 0) { + if (b.lo > n1) { + /* (0 q0) = (n1 n0) / (0 D0) */ + + lsh = CountLeadingZeros(b.lo); + + if (lsh) { + /* + * Normalize, i.e. make the most significant bit of the + * denominator be set. + */ + b.lo = b.lo << lsh; + n1 = (n1 << lsh) | (n0 >> (32 - lsh)); + n0 = n0 << lsh; + } + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + q1 = 0; + + /* remainder is in n0 >> lsh */ + } else { + /* (q1 q0) = (n1 n0) / (0 d0) */ + + if (b.lo == 0) /* user wants to divide by zero! */ + b.lo = 1 / b.lo; /* so go ahead and crash */ + + lsh = CountLeadingZeros(b.lo); + + if (lsh == 0) { + /* + * From (n1 >= b.lo) + * && (the most significant bit of b.lo is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the leading quotient digit q1 = 1). + * + * This special case is necessary, not an optimization + * (Shifts counts of 32 are undefined). + */ + n1 -= b.lo; + q1 = 1; + } else { + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q1, &n1, a, b.lo); + } + + /* n1 != b.lo... */ + + a.lo = n0, a.hi = n1; + norm_udivmod32(&q0, &n0, a, b.lo); + + /* remainder in n0 >> lsh */ + } + + if (rp) { + rp->lo = n0 >> lsh; + rp->hi = 0; + } + } else { + if (b.hi > n1) { + /* (0 0) = (n1 n0) / (D1 d0) */ + + q0 = 0; + q1 = 0; + + /* remainder in (n1 n0) */ + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + /* (0 q0) = (n1 n0) / (d1 d0) */ + + lsh = CountLeadingZeros(b.hi); + if (lsh == 0) { + /* + * From (n1 >= b.hi) + * && (the most significant bit of b.hi is set), + * conclude that + * (the most significant bit of n1 is set) + * && (the quotient digit q0 = 0 or 1). + * + * This special case is necessary, not an optimization. + */ + + /* + * The condition on the next line takes advantage of that + * n1 >= b.hi (true due to control flow). + */ + if (n1 > b.hi || n0 >= b.lo) { + q0 = 1; + a.lo = n0, a.hi = n1; + LL_SUB(a, a, b); + } else { + q0 = 0; + } + q1 = 0; + + if (rp) { + rp->lo = n0; + rp->hi = n1; + } + } else { + PRInt64 m; + + /* + * Normalize. + */ + rsh = 32 - lsh; + + b.hi = (b.hi << lsh) | (b.lo >> rsh); + b.lo = b.lo << lsh; + n2 = n1 >> rsh; + n1 = (n1 << lsh) | (n0 >> rsh); + n0 = n0 << lsh; + + a.lo = n1, a.hi = n2; + norm_udivmod32(&q0, &n1, a, b.hi); + LL_MUL32(m, q0, b.lo); + + if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { + q0--; + LL_SUB(m, m, b); + } + + q1 = 0; + + /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ + if (rp) { + a.lo = n0, a.hi = n1; + LL_SUB(a, a, m); + rp->lo = (a.hi << rsh) | (a.lo >> lsh); + rp->hi = a.hi >> lsh; + } + } + } + } + + if (qp) { + qp->lo = q0; + qp->hi = q1; + } +} +#endif /* !HAVE_LONG_LONG */ diff --git a/pr/src/misc/prnetdb.c b/pr/src/misc/prnetdb.c new file mode 100644 index 00000000..c1728171 --- /dev/null +++ b/pr/src/misc/prnetdb.c @@ -0,0 +1,830 @@ +/* -*- 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 <string.h> + +/* + * On Unix, the error code for gethostbyname() and gethostbyaddr() + * is returned in the global variable h_errno, instead of the usual + * errno. + */ +#if defined(XP_UNIX) +#if defined(_PR_NEED_H_ERRNO) +extern int h_errno; +#endif +#define _MD_GETHOST_ERRNO() h_errno +#else +#define _MD_GETHOST_ERRNO() _MD_ERRNO() +#endif + +#if defined(_PR_NO_PREEMPT) +#define LOCK_DNS() +#define UNLOCK_DNS() +#else +PRLock *_pr_dnsLock = NULL; +#define LOCK_DNS() PR_Lock(_pr_dnsLock) +#define UNLOCK_DNS() PR_Unlock(_pr_dnsLock) +#endif /* defined(_PR_NO_PREEMPT) */ + +#if defined(XP_UNIX) +#include <signal.h> + +/* +** Unix's, as a rule, have a bug in their select code: if a timer +** interrupt occurs and you have SA_RESTART set on your signal, select +** forgets how much time has elapsed and restarts the system call from +** the beginning. This can cause a call to select to *never* time out. +** +** Because we aren't certain that select is wrapped properly in this code +** we disable the clock while a dns operation is occuring. This sucks and +** can be tossed when implement our own dns code that calls our own +** PR_Poll. +*/ + +static sigset_t timer_set; +#define DISABLECLOCK(_set) sigprocmask(SIG_BLOCK, &timer_set, _set) +#define ENABLECLOCK(_set) sigprocmask(SIG_SETMASK, _set, 0) + +#endif /* XP_UNIX */ + +/* + * Some platforms have the reentrant getprotobyname_r() and + * getprotobynumber_r(). However, they come in two flavors. + * Some return a pointer to struct protoent, others return + * an int. + */ + +#if defined(SOLARIS) \ + || (defined(LINUX2_0) && defined(_REENTRANT) \ + && !(defined(__GLIBC__) && __GLIBC__ >= 2)) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_GETPROTO_R_POINTER +#endif + +#if defined(OSF1) || (defined(AIX) && defined(_THREAD_SAFE)) \ + || (defined(HPUX10_10) && defined(_REENTRANT)) \ + || (defined(HPUX10_20) && defined(_REENTRANT)) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_GETPROTO_R_INT +#endif + +#if (defined(LINUX2_0) && defined(__GLIBC__) && __GLIBC__ >= 2) +#define _PR_HAVE_GETPROTO_R +#define _PR_HAVE_5_ARG_GETPROTO_R +#endif + +#if !defined(_PR_HAVE_GETPROTO_R) +PRLock* _getproto_lock = NULL; +#endif + +#if defined(_PR_INET6) +PRBool _pr_ipv6_enabled = PR_FALSE; +#if defined(AIX) +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; +#else +extern const struct in6_addr in6addr_any; +extern const struct in6_addr in6addr_loopback; +#endif /* AIX */ +#endif /* _PR_INET6 */ + +void _PR_InitNet(void) +{ +#if defined(XP_UNIX) +#ifdef HAVE_NETCONFIG + /* + * This one-liner prevents the endless re-open's and re-read's of + * /etc/netconfig on EACH and EVERY call to accept(), connect(), etc. + */ + (void)setnetconfig(); +#endif + sigemptyset(&timer_set); + sigaddset(&timer_set, SIGALRM); +#endif +#if !defined(_PR_NO_PREEMPT) + _pr_dnsLock = PR_NewLock(); +#endif +#if !defined(_PR_HAVE_GETPROTO_R) + _getproto_lock = PR_NewLock(); +#endif + +} + +PR_IMPLEMENT(PRStatus) PR_SetIPv6Enable(PRBool itIs) +{ +#if defined(XP_MAC) +#pragma unused (itIs) +#endif + +#if defined(_PR_INET6) + _pr_ipv6_enabled = itIs; + return PR_SUCCESS; +#else /* defined(_PR_INET6) */ + PR_SetError(PR_PROTOCOL_NOT_SUPPORTED_ERROR, 0); + return PR_FAILURE; +#endif /* defined(_PR_INET6) */ +} /* PR_SetIPv6Enable */ + +PR_IMPLEMENT(PRStatus) PR_GetHostName(char *name, PRUint32 namelen) +{ +#if defined(DEBUG) + static PRBool warn = PR_TRUE; + if (warn) warn = _PR_Obsolete("PR_GetHostName()", "PR_GetSystemInfo()"); +#endif + return PR_GetSystemInfo(PR_SI_HOSTNAME, name, namelen); +} + +/* +** Allocate space from the buffer, aligning it to "align" before doing +** the allocation. "align" must be a power of 2. +*/ +static char *Alloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align) +{ + char *buf = *bufp; + PRIntn buflen = *buflenp; + + if (align && ((long)buf & (align - 1))) { + PRIntn skip = align - ((ptrdiff_t)buf & (align - 1)); + if (buflen < skip) { + return 0; + } + buf += skip; + buflen -= skip; + } + if (buflen < amount) { + return 0; + } + *bufp = buf + amount; + *buflenp = buflen - amount; + return buf; +} + +/* +** Copy a hostent, and all of the memory that it refers to into +** (hopefully) stacked buffers. +*/ +static PRStatus CopyHostent( + struct hostent *from, char *buf, PRIntn bufsize, PRHostEnt *to) +{ + PRIntn len, na; + char **ap; + + /* Do the easy stuff */ + to->h_addrtype = from->h_addrtype; + to->h_length = from->h_length; + + /* Copy the official name */ + if (!from->h_name) return PR_FAILURE; + len = strlen(from->h_name) + 1; + to->h_name = Alloc(len, &buf, &bufsize, 0); + if (!to->h_name) return PR_FAILURE; + memcpy(to->h_name, from->h_name, len); + + /* Count the aliases, then allocate storage for the pointers */ + for (na = 1, ap = from->h_aliases; *ap != 0; na++, ap++){;} /* nothing to execute */ + to->h_aliases = (char**)Alloc( + na * sizeof(char*), &buf, &bufsize, sizeof(char**)); + if (!to->h_aliases) return PR_FAILURE; + + /* Copy the aliases, one at a time */ + for (na = 0, ap = from->h_aliases; *ap != 0; na++, ap++) { + len = strlen(*ap) + 1; + to->h_aliases[na] = Alloc(len, &buf, &bufsize, 0); + if (!to->h_aliases[na]) return PR_FAILURE; + memcpy(to->h_aliases[na], *ap, len); + } + to->h_aliases[na] = 0; + + /* Count the addresses, then allocate storage for the pointers */ + for (na = 1, ap = from->h_addr_list; *ap != 0; na++, ap++){;} /* nothing to execute */ + to->h_addr_list = (char**)Alloc( + na * sizeof(char*), &buf, &bufsize, sizeof(char**)); + if (!to->h_addr_list) return PR_FAILURE; + + /* Copy the addresses, one at a time */ + for (na = 0, ap = from->h_addr_list; *ap != 0; na++, ap++) { + to->h_addr_list[na] = Alloc(to->h_length, &buf, &bufsize, 0); + if (!to->h_addr_list[na]) return PR_FAILURE; + memcpy(to->h_addr_list[na], *ap, to->h_length); + } + to->h_addr_list[na] = 0; + return PR_SUCCESS; +} + +#if !defined(_PR_HAVE_GETPROTO_R) +/* +** Copy a protoent, and all of the memory that it refers to into +** (hopefully) stacked buffers. +*/ +static PRStatus CopyProtoent( + struct protoent *from, char *buf, PRIntn bufsize, PRProtoEnt *to) +{ + PRIntn len, na; + char **ap; + + /* Do the easy stuff */ + to->p_num = from->p_proto; + + /* Copy the official name */ + if (!from->p_name) return PR_FAILURE; + len = strlen(from->p_name) + 1; + to->p_name = Alloc(len, &buf, &bufsize, 0); + if (!to->p_name) return PR_FAILURE; + memcpy(to->p_name, from->p_name, len); + + /* Count the aliases, then allocate storage for the pointers */ + for (na = 1, ap = from->p_aliases; *ap != 0; na++, ap++){;} /* nothing to execute */ + to->p_aliases = (char**)Alloc( + na * sizeof(char*), &buf, &bufsize, sizeof(char**)); + if (!to->p_aliases) return PR_FAILURE; + + /* Copy the aliases, one at a time */ + for (na = 0, ap = from->p_aliases; *ap != 0; na++, ap++) { + len = strlen(*ap) + 1; + to->p_aliases[na] = Alloc(len, &buf, &bufsize, 0); + if (!to->p_aliases[na]) return PR_FAILURE; + memcpy(to->p_aliases[na], *ap, len); + } + to->p_aliases[na] = 0; + + return PR_SUCCESS; +} +#endif /* !defined(_PR_HAVE_GETPROTO_R) */ + +PR_IMPLEMENT(PRStatus) PR_GetHostByName( + const char *name, char *buf, PRIntn bufsize, PRHostEnt *hp) +{ + struct hostent *h; + PRStatus rv = PR_FAILURE; +#ifdef XP_UNIX + sigset_t oldset; +#endif + + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#ifdef XP_UNIX + DISABLECLOCK(&oldset); +#endif + LOCK_DNS(); + +#ifdef _PR_INET6 + if (_pr_ipv6_enabled) + { + h = gethostbyname2(name, AF_INET6); + if (NULL == h) + { + h = gethostbyname2(name, AF_INET); + } + } + else + { + h = gethostbyname(name); + } +#else + h = gethostbyname(name); +#endif /* _PR_INET6 */ + + if (NULL == h) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); + else + { + rv = CopyHostent(h, buf, bufsize, hp); + if (PR_SUCCESS != rv) + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + UNLOCK_DNS(); +#ifdef XP_UNIX + ENABLECLOCK(&oldset); +#endif + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_GetHostByAddr( + const PRNetAddr *hostaddr, char *buf, PRIntn bufsize, PRHostEnt *hostentry) +{ + struct hostent *h; + PRStatus rv = PR_FAILURE; + const void *addr; + int addrlen; +#ifdef XP_UNIX + sigset_t oldset; +#endif + + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#ifdef XP_UNIX + DISABLECLOCK(&oldset); +#endif + LOCK_DNS(); +#if defined(_PR_INET6) + if (hostaddr->raw.family == AF_INET6) + { + addr = &hostaddr->ipv6.ip; + addrlen = sizeof(hostaddr->ipv6.ip); + } + else +#endif /* defined(_PR_INET6) */ + { + PR_ASSERT(hostaddr->raw.family == AF_INET); + addr = &hostaddr->inet.ip; + addrlen = sizeof(hostaddr->inet.ip); + } + + h = gethostbyaddr(addr, addrlen, hostaddr->raw.family); + if (NULL == h) PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); + else + { + rv = CopyHostent(h, buf, bufsize, hostentry); + if (PR_SUCCESS != rv) { + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + } + UNLOCK_DNS(); +#ifdef XP_UNIX + ENABLECLOCK(&oldset); +#endif + return rv; +} + +/******************************************************************************/ +/* + * Some systems define a reentrant version of getprotobyname(). Too bad + * the signature isn't always the same. But hey, they tried. If there + * is such a definition, use it. Otherwise, grab a lock and do it here. + */ +/******************************************************************************/ + +#if !defined(_PR_HAVE_GETPROTO_R) +/* + * This may seem like a silly thing to do, but the compiler SHOULD + * complain if getprotobyname_r() is implemented on some system and + * we're not using it. For sure these signatures are different than + * any usable implementation. + */ + +static struct protoent *getprotobyname_r(const char* name) +{ + return getprotobyname(name); +} /* getprotobyname_r */ + +static struct protoent *getprotobynumber_r(PRInt32 number) +{ + return getprotobynumber(number); +} /* getprotobynumber_r */ + +#endif /* !defined(_PR_HAVE_GETPROTO_R) */ + +PR_IMPLEMENT(PRStatus) PR_GetProtoByName( + const char* name, char* buffer, PRInt32 buflen, PRProtoEnt* result) +{ + PRStatus rv = PR_SUCCESS; + struct protoent* res = (struct protoent*)result; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#if defined(_PR_HAVE_GETPROTO_R_INT) + { + /* + ** The protoent_data has a pointer as the first field. + ** That implies the buffer better be aligned, and char* + ** doesn't promise much. + */ + PRUptrdiff aligned = (PRUptrdiff)buffer; + if (0 != (aligned & (sizeof(struct protoent_data*) - 1))) + { + aligned += sizeof(struct protoent_data*) - 1; + aligned &= ~(sizeof(struct protoent_data*) - 1); + buflen -= (aligned - (PRUptrdiff)buffer); + buffer = (char*)aligned; + } + } +#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */ + + PR_ASSERT(PR_NETDB_BUF_SIZE <= buflen); + if (PR_NETDB_BUF_SIZE > buflen) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if defined(_PR_HAVE_GETPROTO_R_POINTER) + if (NULL == getprotobyname_r(name, res, buffer, buflen)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_GETPROTO_R_INT) + /* + ** The buffer needs to be zero'd, and it should be + ** at least the size of a struct protoent_data. + */ + memset(buffer, 0, buflen); + if (-1 == getprotobyname_r(name, res, (struct protoent_data*)buffer)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_5_ARG_GETPROTO_R) + /* The 5th argument for getprotobyname_r() cannot be NULL */ + if (-1 == getprotobyname_r(name, res, buffer, buflen, &res)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#else /* do it the hard way */ + { + struct protoent *staticBuf; + PR_Lock(_getproto_lock); + staticBuf = getprotobyname_r(name); + if (NULL == staticBuf) + { + rv = PR_FAILURE; + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + } + else + { + rv = CopyProtoent(staticBuf, buffer, buflen, result); + if (PR_FAILURE == rv) + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + PR_Unlock(_getproto_lock); + } +#endif /* all that */ + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_GetProtoByNumber( + PRInt32 number, char* buffer, PRInt32 buflen, PRProtoEnt* result) +{ + PRStatus rv = PR_SUCCESS; + struct protoent* res = (struct protoent*)result; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#if defined(_PR_HAVE_GETPROTO_R_INT) + { + /* + ** The protoent_data has a pointer as the first field. + ** That implies the buffer better be aligned, and char* + ** doesn't promise much. + */ + PRUptrdiff aligned = (PRUptrdiff)buffer; + if (0 != (aligned & (sizeof(struct protoent_data*) - 1))) + { + aligned += sizeof(struct protoent_data*) - 1; + aligned &= ~(sizeof(struct protoent_data*) - 1); + buflen -= (aligned - (PRUptrdiff)buffer); + buffer = (char*)aligned; + } + } +#endif /* defined(_PR_HAVE_GETPROTO_R_INT) */ + + PR_ASSERT(PR_NETDB_BUF_SIZE <= buflen); + if (PR_NETDB_BUF_SIZE > buflen) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#if defined(_PR_HAVE_GETPROTO_R_POINTER) + if (NULL == getprotobynumber_r(number, res, buffer, buflen)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } + +#elif defined(_PR_HAVE_GETPROTO_R_INT) + /* + ** The buffer needs to be zero'd for these OS's. + */ + memset(buffer, 0, buflen); + if (-1 == getprotobynumber_r(number, res, (struct protoent_data*)buffer)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#elif defined(_PR_HAVE_5_ARG_GETPROTO_R) + /* The 5th argument for getprotobynumber_r() cannot be NULL */ + if (-1 == getprotobynumber_r(number, res, buffer, buflen, &res)) + { + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + return PR_FAILURE; + } +#else /* do it the hard way */ + { + struct protoent *staticBuf; + PR_Lock(_getproto_lock); + staticBuf = getprotobynumber_r(number); + if (NULL == staticBuf) + { + rv = PR_FAILURE; + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_ERRNO()); + } + else + { + rv = CopyProtoent(staticBuf, buffer, buflen, result); + if (PR_FAILURE == rv) + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + } + PR_Unlock(_getproto_lock); + } +#endif /* all that crap */ + return rv; + +} + +PR_IMPLEMENT(PRUintn) PR_NetAddrSize(const PRNetAddr* addr) +{ + PRUintn addrsize; + +#if defined(_PR_INET6) + + if ((AF_INET == (0x00ff & addr->raw.family)) + || (AF_INET == (0x00ff & ntohs(addr->raw.family)))) + addrsize = sizeof(struct sockaddr_in); + else if ((AF_INET6 == (0x00ff & addr->raw.family)) + || (AF_INET6 == (0x00ff & ntohs(addr->raw.family)))) + addrsize = sizeof(struct sockaddr_in6); + else addrsize = 0; + +#else /* defined(_PR_INET6) */ + +#if defined(XP_MAC) +#pragma unused (addr) +#endif + + addrsize = sizeof(struct sockaddr_in); + +#endif /* defined(_PR_INET6) */ + + return addrsize; +} /* PR_NetAddrSize */ + +PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt( + PRIntn enumIndex, const PRHostEnt *hostEnt, PRUint16 port, PRNetAddr *address) +{ + void *addr = hostEnt->h_addr_list[enumIndex++]; + memset(address, 0, sizeof(PRNetAddr)); + if (NULL == addr) enumIndex = 0; + else + { +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) + { + address->ipv6.family = AF_INET6; + address->ipv6.port = htons(port); + if (AF_INET6 == hostEnt->h_addrtype) + memcpy(&address->ipv6.ip, addr, hostEnt->h_length); + else + { + unsigned char *start = (unsigned char *) &address->ipv6.ip; + PR_ASSERT(AF_INET == hostEnt->h_addrtype); + memset(start, 0, 10); + memset(start + 10, 0xff, 2); + memcpy(start + 12, addr, hostEnt->h_length); + PR_ASSERT(IN6_IS_ADDR_V4MAPPED(&address->ipv6.ip)); + } + } + else +#endif /* defined(_PR_INET6) */ + { + PR_ASSERT(AF_INET == hostEnt->h_addrtype); + address->inet.family = hostEnt->h_addrtype; + address->inet.port = htons(port); + memcpy(&address->inet.ip, addr, hostEnt->h_length); + } + } + return enumIndex; +} /* PR_EnumerateHostEnt */ + +PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr( + PRNetAddrValue val, PRUint16 port, PRNetAddr *addr) +{ + PRStatus rv = PR_SUCCESS; + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) + { + addr->ipv6.family = AF_INET6; + addr->ipv6.port = htons(port); + switch (val) + { + case PR_IpAddrNull: + break; /* don't overwrite the address */ + case PR_IpAddrAny: + addr->ipv6.ip = in6addr_any; + break; + case PR_IpAddrLoopback: + addr->ipv6.ip = in6addr_loopback; + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + } + else +#endif /* defined(_PR_INET6) */ + { + addr->inet.family = AF_INET; + addr->inet.port = htons(port); + switch (val) + { + case PR_IpAddrNull: + break; /* don't overwrite the address */ + case PR_IpAddrAny: + addr->inet.ip = htonl(INADDR_ANY); + break; + case PR_IpAddrLoopback: + addr->inet.ip = htonl(INADDR_LOOPBACK); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = PR_FAILURE; + } + } + return rv; +} /* PR_InitializeNetAddr */ + +PR_IMPLEMENT(PRNetAddr*) PR_CreateNetAddr(PRNetAddrValue val, PRUint16 port) +{ + PRNetAddr *addr = NULL; + if ((PR_IpAddrAny == val) || (PR_IpAddrLoopback == val)) + { + addr = PR_NEWZAP(PRNetAddr); + if (NULL == addr) + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + else + if (PR_FAILURE == PR_InitializeNetAddr(val, port, addr)) + PR_DELETE(addr); /* and that will make 'addr' == NULL */ + } + else + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return addr; +} /* PR_CreateNetAddr */ + +PR_IMPLEMENT(PRStatus) PR_DestroyNetAddr(PRNetAddr *addr) +{ + PR_Free(addr); + return PR_SUCCESS; +} /* PR_DestroyNetAddr */ + +PR_IMPLEMENT(PRStatus) PR_StringToNetAddr(const char *string, PRNetAddr *addr) +{ + /* + ** If we're built to support IPv6 addressing AND it's currently enabled, + ** then all addresses are of the IPv6 addressing family. Both are required + ** before anything overt happens. + */ + PRStatus status = PR_InitializeNetAddr(PR_IpAddrNull, 0, addr); + + PR_ASSERT(PR_SUCCESS == status); + if (PR_SUCCESS != status) return status; + +#if defined(_PR_INET6) + + if (_pr_ipv6_enabled) + { + /* + ** Okay, we're doing it. + */ + PRIntn rv = inet_pton(AF_INET6, string, &addr->ipv6.ip); + if (1 != rv) + { + /* + * rv is 0 if the string argument is not a valid IPv4 or IPv6 + * address string. + * rv is -1 with errno set to EADNOSUPPORT if the af argument is + * not a known address family. + */ + PRIntn syserrno = (-1 == rv) ? errno : 0; + PR_SetError(PR_INVALID_ARGUMENT_ERROR, syserrno); + status = PR_FAILURE; + } + } + else +#endif + { + PRUint32 *ip = (PRUint32*)&addr->inet.ip; + + *ip = inet_addr(string); + if ((PRUint32) -1 == *ip) + { + /* + * Either the af argument is not AF_INET, or the string argument + * is a malformed address string. + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + status = PR_FAILURE; + } + } + + return status; +} + +PR_IMPLEMENT(PRStatus) PR_NetAddrToString( + const PRNetAddr *addr, char *string, PRUint32 size) +{ + PR_ASSERT(size >= 16); + if (size < 16) goto failed; + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) + { + PR_ASSERT(AF_INET6 == addr->ipv6.family); + if ((AF_INET6 != addr->ipv6.family) + || (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size))) + goto failed; + } + else +#endif /* defined(_PR_INET6) */ + { + PR_ASSERT(AF_INET == addr->inet.family); + if (AF_INET != addr->inet.family) goto failed; + else + { + unsigned char *byte = (unsigned char*)&addr->inet.ip; + PR_snprintf(string, size, "%u.%u.%u.%u", + byte[0], byte[1], byte[2], byte[3]); + } + } + + return PR_SUCCESS; + +failed: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + +} /* PR_NetAddrToString */ + +PR_IMPLEMENT(PRUint16) PR_ntohs(PRUint16 n) { return ntohs(n); } +PR_IMPLEMENT(PRUint32) PR_ntohl(PRUint32 n) { return ntohl(n); } +PR_IMPLEMENT(PRUint16) PR_htons(PRUint16 n) { return htons(n); } +PR_IMPLEMENT(PRUint32) PR_htonl(PRUint32 n) { return htonl(n); } +PR_IMPLEMENT(PRUint64) PR_ntohll(PRUint64 n) +{ + /* + ** There is currently no attempt to optomize out depending + ** on the host' byte order. That would be easy enough to + ** do. + */ + PRUint64 tmp; + PRUint32 hi, lo; + LL_L2UI(lo, n); + LL_SHR(tmp, n, 32); + LL_L2UI(hi, tmp); + hi = PR_ntohl(hi); + lo = PR_ntohl(lo); + LL_UI2L(n, hi); + LL_SHL(n, n, 32); + LL_UI2L(tmp, lo); + LL_ADD(n, n, tmp); + return n; +} /* ntohll */ + +PR_IMPLEMENT(PRUint64) PR_htonll(PRUint64 n) +{ + /* + ** There is currently no attempt to optomize out depending + ** on the host' byte order. That would be easy enough to + ** do. + */ + PRUint64 tmp; + PRUint32 hi, lo; + LL_L2UI(lo, n); + LL_SHR(tmp, n, 32); + LL_L2UI(hi, tmp); + hi = htonl(hi); + lo = htonl(lo); + LL_UI2L(n, hi); + LL_SHL(n, n, 32); + LL_UI2L(tmp, lo); + LL_ADD(n, n, tmp); + return n; +} /* htonll */ + +PR_IMPLEMENT(PRUint16) PR_FamilyInet(void) +{ +#ifdef _PR_INET6 + return (_pr_ipv6_enabled ? AF_INET6 : AF_INET); +#else + return AF_INET; +#endif +} diff --git a/pr/src/misc/prsystem.c b/pr/src/misc/prsystem.c new file mode 100644 index 00000000..cf23e256 --- /dev/null +++ b/pr/src/misc/prsystem.c @@ -0,0 +1,76 @@ +/* -*- 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 "prsystem.h" +#include "prprf.h" + +#if defined(XP_UNIX) +#include <unistd.h> +#include <sys/utsname.h> +#endif + +PR_IMPLEMENT(char) PR_GetDirectorySepartor() +{ + return PR_DIRECTORY_SEPARATOR; +} /* PR_GetDirectorySepartor */ + +PR_IMPLEMENT(PRStatus) PR_GetSystemInfo(PRSysInfo cmd, char *buf, PRUint32 buflen) +{ + PRUintn len = 0; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + switch(cmd) + { + case PR_SI_HOSTNAME: + if (PR_FAILURE == _PR_MD_GETHOSTNAME(buf, (PRUintn)buflen)) + return PR_FAILURE; + /* Return the unqualified hostname */ + while (buf[len] && (len < buflen)) { + if (buf[len] == '.') { + buf[len] = '\0'; + break; + } + len += 1; + } + break; + + case PR_SI_SYSNAME: + /* Return the operating system name */ + (void)PR_snprintf(buf, buflen, _PR_SI_SYSNAME); + break; + + case PR_SI_RELEASE: + /* Return the version of the operating system */ +#if defined(XP_UNIX) + { + struct utsname info; + uname(&info); + (void)PR_snprintf(buf, buflen, info.release); + } +#endif + break; + + case PR_SI_ARCHITECTURE: + /* Return the architecture of the machine (ie. x86, mips, alpha, ...)*/ + (void)PR_snprintf(buf, buflen, _PR_SI_ARCHITECTURE); + break; + } + return PR_SUCCESS; +} diff --git a/pr/src/misc/prthinfo.c b/pr/src/misc/prthinfo.c new file mode 100644 index 00000000..6f6ad1e7 --- /dev/null +++ b/pr/src/misc/prthinfo.c @@ -0,0 +1,229 @@ +/* -*- 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 "prlog.h" +#include "prthread.h" +#ifdef XP_MAC +#include "pprthred.h" +#else +#include "private/pprthred.h" +#endif +#include "primpl.h" + +PR_IMPLEMENT(PRWord *) +PR_GetGCRegisters(PRThread *t, int isCurrent, int *np) +{ + return _MD_HomeGCRegisters(t, isCurrent, np); +} + +PR_IMPLEMENT(PRStatus) +PR_ThreadScanStackPointers(PRThread* t, + PRScanStackFun scanFun, void* scanClosure) +{ + PRThread* current = PR_GetCurrentThread(); + PRWord *sp, *esp, *p0; + int n; + void **ptd; + PRStatus status; + PRUint32 index; + int stack_end; + + /* + ** Store the thread's registers in the thread structure so the GC + ** can scan them. Then scan them. + */ + p0 = _MD_HomeGCRegisters(t, t == current, &n); + status = scanFun(t, (void**)p0, n, scanClosure); + if (status != PR_SUCCESS) + return status; + + /* Scan the C stack for pointers into the GC heap */ +#if defined(XP_PC) && defined(WIN16) + /* + ** Under WIN16, the stack of the current thread is always mapped into + ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan + ** the "task stack". Otherwise, scan the "cached stack" of the inactive + ** thread... + */ + if (t == current) { + sp = (PRWord*) &stack_end; + esp = (PRWord*) _pr_top_of_task_stack; + + PR_ASSERT(sp <= esp); + } else { + sp = (PRWord*) PR_GetSP(t); + esp = (PRWord*) t->stack->stackTop; + + PR_ASSERT((t->stack->stackSize == 0) || + ((sp > (PRWord*)t->stack->stackBottom) && + (sp <= (PRWord*)t->stack->stackTop))); + } +#else /* ! WIN16 */ +#ifdef HAVE_STACK_GROWING_UP + if (t == current) { + esp = (PRWord*) &stack_end; + } else { + esp = (PRWord*) PR_GetSP(t); + } + sp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((esp > (PRWord*)t->stack->stackTop) && + (esp < (PRWord*)t->stack->stackBottom)); + } +#else /* ! HAVE_STACK_GROWING_UP */ + if (t == current) { + sp = (PRWord*) &stack_end; + } else { + sp = (PRWord*) PR_GetSP(t); + } + esp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) && + (sp < (PRWord*)t->stack->stackTop)); + } +#endif /* ! HAVE_STACK_GROWING_UP */ +#endif /* ! WIN16 */ + +#if defined(WIN16) + { + prword_t scan; + prword_t limit; + + scan = (prword_t) sp; + limit = (prword_t) esp; + while (scan < limit) { + prword_t *test; + + test = *((prword_t **)scan); + status = scanFun(t, (void**)&test, 1, scanClosure); + if (status != PR_SUCCESS) + return status; + scan += sizeof(char); + } + } +#else + if (sp < esp) { + status = scanFun(t, (void**)sp, esp - sp, scanClosure); + if (status != PR_SUCCESS) + return status; + } +#endif + + /* + ** Mark all of the per-thread-data items attached to this thread + */ + +#if defined(_PR_PTHREADS) +/* PR_ASSERT(!"I can't do this!"); */ +#else /* defined(_PR_PTHREADS) */ + + /* the execution environment better be accounted for otherwise it will be collected */ + status = scanFun(t, (void**)&t->environment, 1, scanClosure); + if (status != PR_SUCCESS) + return status; + + ptd = t->privateData; + for (index = 0; index < t->tpdLength; index++, ptd++) { + status = scanFun(t, (void**)ptd, 1, scanClosure); + if (status != PR_SUCCESS) + return status; + } +#endif /* defined(_PR_PTHREADS) */ + + return PR_SUCCESS; +} + +/* transducer for PR_EnumerateThreads */ +typedef struct PRScanStackData { + PRScanStackFun scanFun; + void* scanClosure; +} PRScanStackData; + +static PRStatus PR_CALLBACK +pr_ScanStack(PRThread* t, int i, void* arg) +{ +#if defined(XP_MAC) +#pragma unused (i) +#endif + PRScanStackData* data = (PRScanStackData*)arg; + return PR_ThreadScanStackPointers(t, data->scanFun, data->scanClosure); +} + +PR_IMPLEMENT(PRStatus) +PR_ScanStackPointers(PRScanStackFun scanFun, void* scanClosure) +{ + PRScanStackData data; + data.scanFun = scanFun; + data.scanClosure = scanClosure; + return PR_EnumerateThreads(pr_ScanStack, &data); +} + +PR_PUBLIC_API(PRUword) +PR_GetStackSpaceLeft(PRThread* t) +{ + PRThread *current = PR_CurrentThread(); + PRWord *sp, *esp; + int stack_end; + +#if defined(WIN16) + /* + ** Under WIN16, the stack of the current thread is always mapped into + ** the "task stack" (at SS:xxxx). So, if t is the current thread, scan + ** the "task stack". Otherwise, scan the "cached stack" of the inactive + ** thread... + */ + if (t == current) { + sp = (PRWord*) &stack_end; + esp = (PRWord*) _pr_top_of_task_stack; + + PR_ASSERT(sp <= esp); + } else { + sp = (PRWord*) PR_GetSP(t); + esp = (PRWord*) t->stack->stackTop; + + PR_ASSERT((t->stack->stackSize == 0) || + ((sp > (PRWord*)t->stack->stackBottom) && + (sp <= (PRWord*)t->stack->stackTop))); + } +#else /* ! WIN16 */ +#ifdef HAVE_STACK_GROWING_UP + if (t == current) { + esp = (PRWord*) &stack_end; + } else { + esp = (PRWord*) PR_GetSP(t); + } + sp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((esp > (PRWord*)t->stack->stackTop) && + (esp < (PRWord*)t->stack->stackBottom)); + } +#else /* ! HAVE_STACK_GROWING_UP */ + if (t == current) { + sp = (PRWord*) &stack_end; + } else { + sp = (PRWord*) PR_GetSP(t); + } + esp = (PRWord*) t->stack->stackTop; + if (t->stack->stackSize) { + PR_ASSERT((sp > (PRWord*)t->stack->stackBottom) && + (sp < (PRWord*)t->stack->stackTop)); + } +#endif /* ! HAVE_STACK_GROWING_UP */ +#endif /* ! WIN16 */ + return (PRUword)t->stack->stackSize - ((PRWord)esp - (PRWord)sp); +} diff --git a/pr/src/misc/prtime.c b/pr/src/misc/prtime.c new file mode 100644 index 00000000..bfe36088 --- /dev/null +++ b/pr/src/misc/prtime.c @@ -0,0 +1,1930 @@ +/* -*- 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. + */ + +/* + * prtime.c -- + * + * NSPR date and time functions + * + */ + +#include "prinit.h" +#include "prtime.h" +#include "prlock.h" +#include "prprf.h" +#include "prlog.h" + +#include <string.h> +#include <ctype.h> + +#ifdef XP_MAC +#include <time.h> +#endif + + + + +/* + * Static variables used by functions in this file + */ + +/* + * The following array contains the day of year for the last day of + * each month, where index 1 is January, and day 0 is January 1. + */ + +static int lastDayOfMonth[2][13] = { + {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364}, + {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365} +}; + +/* + * The number of days in a month + */ + +static PRInt8 nDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +/* + * Declarations for internal functions defined later in this file. + */ + +static void ComputeGMT(PRTime time, PRExplodedTime *gmt); +static int IsLeapYear(PRInt16 year); +static void ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset); + +/* + *------------------------------------------------------------------------ + * + * ComputeGMT -- + * + * Caveats: + * - we ignore leap seconds + * - our leap-year calculation is only correct for years 1901-2099 + * + *------------------------------------------------------------------------ + */ + +static void +ComputeGMT(PRTime time, PRExplodedTime *gmt) +{ + PRInt32 tmp, rem; + PRInt32 numDays; + PRInt64 numDays64, rem64; + int isLeap; + PRInt64 sec; + PRInt64 usec; + PRInt64 usecPerSec; + PRInt64 secPerDay; + + /* + * We first do the usec, sec, min, hour thing so that we do not + * have to do LL arithmetic. + */ + + LL_I2L(usecPerSec, 1000000L); + LL_DIV(sec, time, usecPerSec); + LL_MOD(usec, time, usecPerSec); + LL_L2I(gmt->tm_usec, usec); + /* Correct for weird mod semantics so the remainder is always positive */ + if (gmt->tm_usec < 0) { + PRInt64 one; + + LL_I2L(one, 1L); + LL_SUB(sec, sec, one); + gmt->tm_usec += 1000000L; + } + + LL_I2L(secPerDay, 86400L); + LL_DIV(numDays64, sec, secPerDay); + LL_MOD(rem64, sec, secPerDay); + /* We are sure both of these numbers can fit into PRInt32 */ + LL_L2I(numDays, numDays64); + LL_L2I(rem, rem64); + if (rem < 0) { + numDays--; + rem += 86400L; + } + + /* Compute day of week. Epoch started on a Thursday. */ + + gmt->tm_wday = (numDays + 4) % 7; + if (gmt->tm_wday < 0) { + gmt->tm_wday += 7; + } + + /* Compute the time of day. */ + + gmt->tm_hour = rem / 3600; + rem %= 3600; + gmt->tm_min = rem / 60; + gmt->tm_sec = rem % 60; + + /* Compute the four-year span containing the specified time */ + + tmp = numDays / (4 * 365 + 1); + rem = numDays % (4 * 365 + 1); + + if (rem < 0) { + tmp--; + rem += (4 * 365 + 1); + } + + /* + * Compute the year after 1900 by taking the four-year span and + * adjusting for the remainder. This works because 2000 is a + * leap year, and 1900 and 2100 are out of the range. + */ + + tmp = (tmp * 4) + 1970; + isLeap = 0; + + /* + * 1970 has 365 days + * 1971 has 365 days + * 1972 has 366 days (leap year) + * 1973 has 365 days + */ + + if (rem >= 365) { /* 1971, etc. */ + tmp++; + rem -= 365; + if (rem >= 365) { /* 1972, etc. */ + tmp++; + rem -= 365; + if (rem >= 366) { /* 1973, etc. */ + tmp++; + rem -= 366; + } else { + isLeap = 1; + } + } + } + + gmt->tm_year = tmp; + gmt->tm_yday = rem; + + /* Compute the month and day of month. */ + + for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) { + } + gmt->tm_month = --tmp; + gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp]; + + gmt->tm_params.tp_gmt_offset = 0; + gmt->tm_params.tp_dst_offset = 0; +} + + +/* + *------------------------------------------------------------------------ + * + * PR_ExplodeTime -- + * + * Cf. struct tm *gmtime(const time_t *tp) and + * struct tm *localtime(const time_t *tp) + * + *------------------------------------------------------------------------ + */ + +PR_IMPLEMENT(void) +PR_ExplodeTime( + PRTime usecs, + PRTimeParamFn params, + PRExplodedTime *exploded) +{ + ComputeGMT(usecs, exploded); + exploded->tm_params = params(exploded); + ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset + + exploded->tm_params.tp_dst_offset); +} + + +/* + *------------------------------------------------------------------------ + * + * PR_ImplodeTime -- + * + * Cf. time_t mktime(struct tm *tp) + * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough. + * + *------------------------------------------------------------------------ + */ +#if defined(HAVE_WATCOM_BUG_2) +PRTime __pascal __export __loadds +#else +PR_IMPLEMENT(PRTime) +#endif +PR_ImplodeTime(const PRExplodedTime *exploded) +{ + PRExplodedTime copy; + PRTime retVal; + PRInt64 secPerDay, usecPerSec; + PRInt64 temp; + PRInt64 numSecs64; + PRInt32 fourYears; + PRInt32 remainder; + PRInt32 numDays; + PRInt32 numSecs; + + /* Normalize first. Do this on our copy */ + copy = *exploded; + PR_NormalizeTime(©, PR_GMTParameters); + + fourYears = (copy.tm_year - 1970) / 4; + remainder = (copy.tm_year - 1970) % 4; + if (remainder < 0) { + remainder += 4; + fourYears--; + } + numDays = fourYears * (4 * 365 + 1); + switch (remainder) { + case 0: + break; + case 1: /* 1970 */ + numDays += 365; + break; + case 2: /* 1970-1 */ + numDays += 365 * 2; + break; + case 3: /* 1970-2 */ + numDays += 365 * 3 + 1; + break; + } + + numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600 + + copy.tm_min * 60 + copy.tm_sec; + + LL_I2L(temp, numDays); + LL_I2L(secPerDay, 86400); + LL_MUL(temp, temp, secPerDay); + LL_I2L(numSecs64, numSecs); + LL_ADD(numSecs64, numSecs64, temp); + + /* apply the GMT and DST offsets */ + LL_I2L(temp, copy.tm_params.tp_gmt_offset); + LL_SUB(numSecs64, numSecs64, temp); + LL_I2L(temp, copy.tm_params.tp_dst_offset); + LL_SUB(numSecs64, numSecs64, temp); + + LL_I2L(usecPerSec, 1000000L); + LL_MUL(temp, numSecs64, usecPerSec); + LL_I2L(retVal, copy.tm_usec); + LL_ADD(retVal, retVal, temp); + + return retVal; +} + +/* + *------------------------------------------------------------------------- + * + * IsLeapYear -- + * + * Returns 1 if the year is a leap year, 0 otherwise. + * + *------------------------------------------------------------------------- + */ + +static int IsLeapYear(PRInt16 year) +{ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + return 1; + else + return 0; +} + +/* + * 'secOffset' should be less than 86400 (i.e., a day). + * 'time' should point to a normalized PRExplodedTime. + */ + +static void +ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset) +{ + time->tm_sec += secOffset; + + /* Note that in this implementation we do not count leap seconds */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0) { + /* Decrement mday, yday, and wday */ + time->tm_hour += 24; + time->tm_mday--; + time->tm_yday--; + if (time->tm_mday < 1) { + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + if (IsLeapYear(time->tm_year)) + time->tm_yday = 365; + else + time->tm_yday = 364; + } + time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + time->tm_wday--; + if (time->tm_wday < 0) + time->tm_wday = 6; + } else if (time->tm_hour > 23) { + /* Increment mday, yday, and wday */ + time->tm_hour -= 24; + time->tm_mday++; + time->tm_yday++; + if (time->tm_mday > + nDays[IsLeapYear(time->tm_year)][time->tm_month]) { + time->tm_mday = 1; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + time->tm_yday = 0; + } + } + time->tm_wday++; + if (time->tm_wday > 6) + time->tm_wday = 0; + } +} + +PR_IMPLEMENT(void) +PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params) +{ + int daysInMonth; + PRInt32 fourYears; + PRInt32 remainder; + PRInt32 numDays; + + /* Get back to GMT */ + time->tm_sec -= time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset; + time->tm_params.tp_gmt_offset = 0; + time->tm_params.tp_dst_offset = 0; + + /* Now normalize GMT */ + + if (time->tm_usec < 0 || time->tm_usec >= 1000000) { + time->tm_sec += time->tm_usec / 1000000; + time->tm_usec %= 1000000; + if (time->tm_usec < 0) { + time->tm_usec += 1000000; + time->tm_sec--; + } + } + + /* Note that we do not count leap seconds in this implementation */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0 || time->tm_hour >= 24) { + time->tm_mday += time->tm_hour / 24; + time->tm_hour %= 24; + if (time->tm_hour < 0) { + time->tm_hour += 24; + time->tm_mday--; + } + } + + /* Normalize month and year before mday */ + if (time->tm_month < 0 || time->tm_month >= 12) { + time->tm_year += time->tm_month / 12; + time->tm_month %= 12; + if (time->tm_month < 0) { + time->tm_month += 12; + time->tm_year--; + } + } + + /* Now that month and year are in proper range, normalize mday */ + + if (time->tm_mday < 1) { + /* mday too small */ + do { + /* the previous month */ + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + } + time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } while (time->tm_mday < 1); + } else { + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + while (time->tm_mday > daysInMonth) { + /* mday too large */ + time->tm_mday -= daysInMonth; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + } + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + } + + /* Recompute yday and wday */ + time->tm_yday = time->tm_mday + + lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month]; + fourYears = (time->tm_year - 1970) / 4; + remainder = (time->tm_year - 1970) % 4; + if (remainder < 0) { + remainder += 4; + fourYears--; + } + numDays = fourYears * (4 * 365 + 1); + switch (remainder) { + case 0: + break; + case 1: + numDays += 365; /* 1970 */ + break; + case 2: + numDays += 365 + 365; /* 1970 and 1971 */ + break; + case 3: + numDays += 365 + 365 + 366; /* 1970-2 */ + } + numDays += time->tm_yday; + time->tm_wday = (numDays + 4) % 7; + if (time->tm_wday < 0) { + time->tm_wday += 7; + } + + /* Recompute time parameters */ + + time->tm_params = params(time); + + ApplySecOffset(time, time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset); +} + + +/* + *------------------------------------------------------------------------- + * + * PR_LocalTimeParameters -- + * + * returns the time parameters for the local time zone + * + * The following uses localtime() from the standard C library. + * (time.h) This is our fallback implementation. Unix and PC + * use this version. Mac has its own machine-dependent + * implementation of this function. + * + *------------------------------------------------------------------------- + */ + +#include <time.h> + +#if (defined(OSF1) && !defined(OSF1V4)) || defined(HPUX10_10) \ + || defined(HPUX10_20) + +#define MT_safe_localtime(timer, result) \ + (localtime_r(timer, result) == 0 ? result : NULL) + +#elif defined(SOLARIS) || defined(IRIX) \ + || (defined(AIX) && !defined(AIX4_1) && defined(_THREAD_SAFE)) \ + || defined(OSF1V4) \ + || defined(HPUX10_30) || defined(HPUX11) + +#define MT_safe_localtime localtime_r + +#else + +#if defined(XP_MAC) +extern struct tm *Maclocaltime(const time_t * t); +#endif + +static PRLock *monitor = NULL; + +static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result) +{ + struct tm *tmPtr; + int needLock = PR_Initialized(); /* We need to use a lock to protect + * against NSPR threads only when the + * NSPR thread system is activated. */ + + if (needLock) { + if (monitor == NULL) { + monitor = PR_NewLock(); + } + PR_Lock(monitor); + } + + /* + * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock' + * represents a time before midnight January 1, 1970. In + * that case, we also return a NULL pointer and the struct tm + * object pointed to by 'result' is not modified. + * + * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long + * hence, does not recognize negative values of clock as pre-1/1/70. + * We have to manually check (WIN16 only) for negative value of + * clock and return NULL. + */ + +#if defined(XP_MAC) + tmPtr = Maclocaltime(clock); +#else + tmPtr = localtime(clock); +#endif + +#if defined(WIN16) + if ( (PRInt32) *clock < 0 ) + result = NULL; + else + *result = *tmPtr; +#else + if (tmPtr) { + *result = *tmPtr; + } else { + result = NULL; + } +#endif /* WIN16 */ + + if (needLock) PR_Unlock(monitor); + + return result; +} + +#endif /* definition of MT_safe_localtime() */ + +#if defined(XP_UNIX) || defined(XP_PC) + +PR_IMPLEMENT(PRTimeParameters) +PR_LocalTimeParameters(const PRExplodedTime *gmt) +{ + + PRTimeParameters retVal; + struct tm localTime; + time_t secs; + PRTime secs64; + PRInt64 usecPerSec; + PRInt64 maxInt32; + PRInt32 dayOffset; + PRInt32 offset2Jan1970; + PRInt32 offsetNew; + int isdst2Jan1970; + + /* + * Calculate the GMT offset. First, figure out what is + * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400 + * seconds, since the epoch) in local time. Then we calculate + * the difference between local time and GMT in seconds: + * gmt_offset = local_time - GMT + * + * Caveat: the validity of this calculation depends on two + * assumptions: + * 1. Daylight saving time was not in effect on Jan. 2, 1970. + * 2. The time zone of the geographic location has not changed + * since Jan. 2, 1970. + */ + + secs = 86400L; + (void) MT_safe_localtime(&secs, &localTime); + + /* GMT is 00:00:00, 2nd of Jan. */ + + offset2Jan1970 = (PRInt32)localTime.tm_sec + + 60L * (PRInt32)localTime.tm_min + + 3600L * (PRInt32)localTime.tm_hour + + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L); + + isdst2Jan1970 = localTime.tm_isdst; + + /* + * Now compute DST offset. We calculate the overall offset + * of local time from GMT, similar to above. The overall + * offset has two components: gmt offset and dst offset. + * We subtract gmt offset from the overall offset to get + * the dst offset. + * overall_offset = local_time - GMT + * overall_offset = gmt_offset + dst_offset + * ==> dst_offset = local_time - GMT - gmt_offset + */ + + secs64 = PR_ImplodeTime(gmt); /* This is still in microseconds */ + LL_I2L(usecPerSec, PR_USEC_PER_SEC); + LL_DIV(secs64, secs64, usecPerSec); /* Convert to seconds */ + LL_I2L(maxInt32, 0x7fffffff); + if (LL_CMP(secs64, >, maxInt32)) { + /* secs64 is too large for time_t (32-bit integer) */ + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = 0; + return retVal; + } + LL_L2I(secs, secs64); + + /* + * On Windows, localtime() (and our MT_safe_localtime() too) + * returns a NULL pointer for time before midnight January 1, + * 1970 GMT. In that case, we just use the GMT offset for + * Jan 2, 1970 and assume that DST was not in effect. + */ + + if (MT_safe_localtime(&secs, &localTime) == NULL) { + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = 0; + return retVal; + } + + /* + * dayOffset is the offset between local time and GMT in + * the day component, which can only be -1, 0, or 1. We + * use the day of the week to compute dayOffset. + */ + + dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday; + + /* + * Need to adjust for wrapping around of day of the week from + * 6 back to 0. + */ + + if (dayOffset == -6) { + /* Local time is Sunday (0) and GMT is Saturday (6) */ + dayOffset = 1; + } else if (dayOffset == 6) { + /* Local time is Saturday (6) and GMT is Sunday (0) */ + dayOffset = -1; + } + + offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec + + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min) + + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour) + + 86400L * (PRInt32)dayOffset; + + if (localTime.tm_isdst <= 0) { + /* DST is not in effect */ + retVal.tp_gmt_offset = offsetNew; + retVal.tp_dst_offset = 0; + } else { + /* DST is in effect */ + if (isdst2Jan1970 <=0) { + /* + * DST was not in effect back in 2 Jan. 1970. + * Use the offset back then as the GMT offset, + * assuming the time zone has not changed since then. + */ + retVal.tp_gmt_offset = offset2Jan1970; + retVal.tp_dst_offset = offsetNew - offset2Jan1970; + } else { + /* + * DST was also in effect back in 2 Jan. 1970. + * Then our clever trick (or rather, ugly hack) fails. + * We will just assume DST offset is an hour. + */ + retVal.tp_gmt_offset = offsetNew - 3600; + retVal.tp_dst_offset = 3600; + } + } + + return retVal; +} + +#endif /* defined(XP_UNIX) !! defined(XP_PC) */ + +/* + *------------------------------------------------------------------------ + * + * PR_USPacificTimeParameters -- + * + * The time parameters function for the US Pacific Time Zone. + * + *------------------------------------------------------------------------ + */ + +PR_IMPLEMENT(PRTimeParameters) +PR_USPacificTimeParameters(const PRExplodedTime *gmt) +{ + PRTimeParameters retVal; + PRExplodedTime std; + + /* + * Based on geographic location and GMT, figure out offset of + * standard time from GMT. In this example implementation, we + * assume the local time zone is US Pacific Time. + */ + + retVal.tp_gmt_offset = -8L * 3600L; + + /* + * Make a copy of GMT. Note that the tm_params field of this copy + * is ignored. + */ + + std.tm_usec = gmt->tm_usec; + std.tm_sec = gmt->tm_sec; + std.tm_min = gmt->tm_min; + std.tm_hour = gmt->tm_hour; + std.tm_mday = gmt->tm_mday; + std.tm_month = gmt->tm_month; + std.tm_year = gmt->tm_year; + std.tm_wday = gmt->tm_wday; + std.tm_yday = gmt->tm_yday; + + /* Apply the offset to GMT to obtain the local standard time */ + ApplySecOffset(&std, retVal.tp_gmt_offset); + + /* + * Apply the rules on standard time or GMT to obtain daylight saving + * time offset. In this implementation, we use the US DST rule. + */ + if (std.tm_month < 3) { + retVal.tp_dst_offset = 0L; + } else if (std.tm_month == 3) { + if (std.tm_wday == 0) { + /* A Sunday */ + if (std.tm_mday <= 7) { + /* First Sunday */ + /* 01:59:59 PST -> 03:00:00 PDT */ + if (std.tm_hour < 2) { + retVal.tp_dst_offset = 0L; + } else { + retVal.tp_dst_offset = 3600L; + } + } else { + /* Not first Sunday */ + retVal.tp_dst_offset = 3600L; + } + } else { + /* Not a Sunday. See if before first Sunday or after */ + if (std.tm_wday + 1 <= std.tm_mday) { + /* After first Sunday */ + retVal.tp_dst_offset = 3600L; + } else { + /* Before first Sunday */ + retVal.tp_dst_offset = 0L; + } + } + } else if (std.tm_month < 9) { + retVal.tp_dst_offset = 3600L; + } else if (std.tm_month == 9) { + if (std.tm_wday == 0) { + if (31 - std.tm_mday < 7) { + /* Last Sunday */ + /* 01:59:59 PDT -> 01:00:00 PST */ + if (std.tm_hour < 1) { + retVal.tp_dst_offset = 3600L; + } else { + retVal.tp_dst_offset = 0L; + } + } else { + /* Not last Sunday */ + retVal.tp_dst_offset = 3600L; + } + } else { + /* See if before or after last Sunday */ + if (7 - std.tm_wday <= 31 - std.tm_mday) { + /* before last Sunday */ + retVal.tp_dst_offset = 3600L; + } else { + retVal.tp_dst_offset = 0L; + } + } + } else { + retVal.tp_dst_offset = 0L; + } + return retVal; +} + +/* + *------------------------------------------------------------------------ + * + * PR_GMTParameters -- + * + * Returns the PRTimeParameters for Greenwich Mean Time. + * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0. + * + *------------------------------------------------------------------------ + */ + +PR_IMPLEMENT(PRTimeParameters) +PR_GMTParameters(const PRExplodedTime *gmt) +{ +#if defined(XP_MAC) +#pragma unused (gmt) +#endif + + PRTimeParameters retVal = { 0, 0 }; + return retVal; +} + +/* + * The following code implements PR_ParseTimeString(). It is based on + * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>. + */ + +/* + * We only recognize the abbreviations of a small subset of time zones + * in North America, Europe, and Japan. + * + * PST/PDT: Pacific Standard/Daylight Time + * MST/MDT: Mountain Standard/Daylight Time + * CST/CDT: Central Standard/Daylight Time + * EST/EDT: Eastern Standard/Daylight Time + * AST: Atlantic Standard Time + * NST: Newfoundland Standard Time + * GMT: Greenwich Mean Time + * BST: British Summer Time + * MET: Middle Europe Time + * EET: Eastern Europe Time + * JST: Japan Standard Time + */ + +typedef enum +{ + TT_UNKNOWN, + + TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT, + + TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN, + TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC, + + TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT, + TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST +} TIME_TOKEN; + +/* + * This parses a time/date string into a PRTime + * (microseconds after "1-Jan-1970 00:00:00 GMT"). + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + * + * Many formats are handled, including: + * + * 14 Apr 89 03:20:12 + * 14 Apr 89 03:20 GMT + * Fri, 17 Mar 89 4:01:33 + * Fri, 17 Mar 89 4:01 GMT + * Mon Jan 16 16:12 PDT 1989 + * Mon Jan 16 16:12 +0130 1989 + * 6 May 1992 16:41-JST (Wednesday) + * 22-AUG-1993 10:59:12.82 + * 22-AUG-1993 10:59pm + * 22-AUG-1993 12:59am + * 22-AUG-1993 12:59 PM + * Friday, August 04, 1995 3:54 PM + * 06/21/95 04:24:34 PM + * 20/06/95 21:07 + * 95-06-08 19:32:48 EDT + * + * If the input string doesn't contain a description of the timezone, + * we consult the `default_to_gmt' to decide whether the string should + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). + * The correct value for this argument depends on what standard specified + * the time string which you are parsing. + */ + +PR_IMPLEMENT(PRStatus) +PR_ParseTimeString( + const char *string, + PRBool default_to_gmt, + PRTime *result) +{ + PRExplodedTime tm; + TIME_TOKEN dotw = TT_UNKNOWN; + TIME_TOKEN month = TT_UNKNOWN; + TIME_TOKEN zone = TT_UNKNOWN; + int zone_offset = -1; + int date = -1; + PRInt32 year = -1; + int hour = -1; + int min = -1; + int sec = -1; + + const char *rest = string; + +#ifdef DEBUG + int iterations = 0; +#endif + + PR_ASSERT(string && result); + if (!string || !result) return PR_FAILURE; + + while (*rest) + { + +#ifdef DEBUG + if (iterations++ > 1000) + { + PR_ASSERT(0); + return PR_FAILURE; + } +#endif + + switch (*rest) + { + case 'a': case 'A': + if (month == TT_UNKNOWN && + (rest[1] == 'p' || rest[1] == 'P') && + (rest[2] == 'r' || rest[2] == 'R')) + month = TT_APR; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 's') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_AST; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'g' || rest[2] == 'G')) + month = TT_AUG; + break; + case 'b': case 'B': + if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_BST; + break; + case 'c': case 'C': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_CDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_CST; + break; + case 'd': case 'D': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'c' || rest[2] == 'C')) + month = TT_DEC; + break; + case 'e': case 'E': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EET; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EST; + break; + case 'f': case 'F': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'b' || rest[2] == 'B')) + month = TT_FEB; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'r' || rest[1] == 'R') && + (rest[2] == 'i' || rest[2] == 'I')) + dotw = TT_FRI; + break; + case 'g': case 'G': + if (zone == TT_UNKNOWN && + (rest[1] == 'm' || rest[1] == 'M') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_GMT; + break; + case 'j': case 'J': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'n' || rest[2] == 'N')) + month = TT_JAN; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_JST; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'l' || rest[2] == 'L')) + month = TT_JUL; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) + month = TT_JUN; + break; + case 'm': case 'M': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'r' || rest[2] == 'R')) + month = TT_MAR; + else if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'y' || rest[2] == 'Y')) + month = TT_MAY; + else if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MET; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'n' || rest[2] == 'N')) + dotw = TT_MON; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MST; + break; + case 'n': case 'N': + if (month == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'v' || rest[2] == 'V')) + month = TT_NOV; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_NST; + break; + case 'o': case 'O': + if (month == TT_UNKNOWN && + (rest[1] == 'c' || rest[1] == 'C') && + (rest[2] == 't' || rest[2] == 'T')) + month = TT_OCT; + break; + case 'p': case 'P': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_PDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_PST; + break; + case 's': case 'S': + if (dotw == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 't' || rest[2] == 'T')) + dotw = TT_SAT; + else if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'p' || rest[2] == 'P')) + month = TT_SEP; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) + dotw = TT_SUN; + break; + case 't': case 'T': + if (dotw == TT_UNKNOWN && + (rest[1] == 'h' || rest[1] == 'H') && + (rest[2] == 'u' || rest[2] == 'U')) + dotw = TT_THU; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'e' || rest[2] == 'E')) + dotw = TT_TUE; + break; + case 'u': case 'U': + if (zone == TT_UNKNOWN && + (rest[1] == 't' || rest[1] == 'T') && + !(rest[2] >= 'A' && rest[2] <= 'Z') && + !(rest[2] >= 'a' && rest[2] <= 'z')) + /* UT is the same as GMT but UTx is not. */ + zone = TT_GMT; + break; + case 'w': case 'W': + if (dotw == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'd' || rest[2] == 'D')) + dotw = TT_WED; + break; + + case '+': case '-': + { + const char *end; + int sign; + if (zone_offset >= 0) + { + /* already got one... */ + rest++; + break; + } + if (zone != TT_UNKNOWN && zone != TT_GMT) + { + /* GMT+0300 is legal, but PST+0300 is not. */ + rest++; + break; + } + + sign = ((*rest == '+') ? 1 : -1); + rest++; /* move over sign */ + end = rest; + while (*end >= '0' && *end <= '9') + end++; + if (rest == end) /* no digits here */ + break; + + if ((end - rest) == 4) + /* offset in HHMM */ + zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) + + (((rest[2]-'0')*10) + (rest[3]-'0'))); + else if ((end - rest) == 2) + /* offset in hours */ + zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60; + else if ((end - rest) == 1) + /* offset in hours */ + zone_offset = (rest[0]-'0') * 60; + else + /* 3 or >4 */ + break; + + zone_offset *= sign; + zone = TT_GMT; + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int tmp_hour = -1; + int tmp_min = -1; + int tmp_sec = -1; + const char *end = rest + 1; + while (*end >= '0' && *end <= '9') + end++; + + /* end is now the first character after a range of digits. */ + + if (*end == ':') + { + if (hour > 0 && min > 0) /* already got it */ + break; + + /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */ + if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if (rest[1] != ':' && + rest[2] != ':') + /* it is not [0-9]: or [0-9][0-9]: */ + break; + else if ((end - rest) == 2) + tmp_hour = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_hour = (rest[0]-'0'); + + while (*rest && *rest != ':') + rest++; + rest++; + + /* move over the colon, and parse minutes */ + + end = rest + 1; + while (*end >= '0' && *end <= '9') + end++; + + if (end == rest) + /* no digits after first colon? */ + break; + else if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if ((end - rest) == 2) + tmp_min = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_min = (rest[0]-'0'); + + /* now go for seconds */ + rest = end; + if (*rest == ':') + rest++; + end = rest; + while (*end >= '0' && *end <= '9') + end++; + + if (end == rest) + /* no digits after second colon - that's ok. */ + ; + else if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if ((end - rest) == 2) + tmp_sec = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_sec = (rest[0]-'0'); + + /* If we made it here, we've parsed hour and min, + and possibly sec, so it worked as a unit. */ + + /* skip over whitespace and see if there's an AM or PM + directly following the time. + */ + if (tmp_hour <= 12) + { + const char *s = end; + while (*s && (*s == ' ' || *s == '\t')) + s++; + if ((s[0] == 'p' || s[0] == 'P') && + (s[1] == 'm' || s[1] == 'M')) + /* 10:05pm == 22:05, and 12:05pm == 12:05 */ + tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12); + else if (tmp_hour == 12 && + (s[0] == 'a' || s[0] == 'A') && + (s[1] == 'm' || s[1] == 'M')) + /* 12:05am == 00:05 */ + tmp_hour = 0; + } + + hour = tmp_hour; + min = tmp_min; + sec = tmp_sec; + rest = end; + break; + } + else if ((*end == '/' || *end == '-') && + end[1] >= '0' && end[1] <= '9') + { + /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95 + or even 95-06-05... + #### But it doesn't handle 1995-06-22. + */ + int n1, n2, n3; + const char *s; + + if (month != TT_UNKNOWN) + /* if we saw a month name, this can't be. */ + break; + + s = rest; + + n1 = (*s++ - '0'); /* first 1 or 2 digits */ + if (*s >= '0' && *s <= '9') + n1 = n1*10 + (*s++ - '0'); + + if (*s != '/' && *s != '-') /* slash */ + break; + s++; + + if (*s < '0' || *s > '9') /* second 1 or 2 digits */ + break; + n2 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') + n2 = n2*10 + (*s++ - '0'); + + if (*s != '/' && *s != '-') /* slash */ + break; + s++; + + if (*s < '0' || *s > '9') /* third 1, 2, or 4 digits */ + break; + n3 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') + n3 = n3*10 + (*s++ - '0'); + + if (*s >= '0' && *s <= '9') /* optional digits 3 and 4 */ + { + n3 = n3*10 + (*s++ - '0'); + if (*s < '0' || *s > '9') + break; + n3 = n3*10 + (*s++ - '0'); + } + + if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */ + (*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z')) + break; + + /* Ok, we parsed three 1-2 digit numbers, with / or - + between them. Now decide what the hell they are + (DD/MM/YY or MM/DD/YY or YY/MM/DD.) + */ + + if (n1 > 70) /* must be YY/MM/DD */ + { + if (n2 > 12) break; + if (n3 > 31) break; + year = n1; + if (year < 1900) year += 1900; + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + date = n3; + rest = s; + break; + } + + if (n3 < 70 || /* before epoch - can't represent it. */ + (n1 > 12 && n2 > 12)) /* illegal */ + { + rest = s; + break; + } + + if (n3 < 1900) n3 += 1900; + + if (n1 > 12) /* must be DD/MM/YY */ + { + date = n1; + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + year = n3; + } + else /* assume MM/DD/YY */ + { + /* #### In the ambiguous case, should we consult the + locale to find out the local default? */ + month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1); + date = n2; + year = n3; + } + rest = s; + } + else if ((*end >= 'A' && *end <= 'Z') || + (*end >= 'a' && *end <= 'z')) + /* Digits followed by non-punctuation - what's that? */ + ; + else if ((end - rest) == 4) /* four digits is a year */ + year = (year < 0 + ? ((rest[0]-'0')*1000L + + (rest[1]-'0')*100L + + (rest[2]-'0')*10L + + (rest[3]-'0')) + : year); + else if ((end - rest) == 2) /* two digits - date or year */ + { + int n = ((rest[0]-'0')*10 + + (rest[1]-'0')); + /* If we don't have a date (day of the month) and we see a number + less than 32, then assume that is the date. + + Otherwise, if we have a date and not a year, assume this is the + year. If it is less than 70, then assume it refers to the 21st + century. If it is two digits (>= 70), assume it refers to this + century. Otherwise, assume it refers to an unambiguous year. + + The world will surely end soon. + */ + if (date < 0 && n < 32) + date = n; + else if (year < 0) + { + if (n < 70) + year = 2000 + n; + else if (n < 100) + year = 1900 + n; + else + year = n; + } + /* else what the hell is this. */ + } + else if ((end - rest) == 1) /* one digit - date */ + date = (date < 0 ? (rest[0]-'0') : date); + /* else, three or more than four digits - what's that? */ + + break; + } + } + + /* Skip to the end of this token, whether we parsed it or not. + Tokens are delimited by whitespace, or ,;-/ + But explicitly not :+-. + */ + while (*rest && + *rest != ' ' && *rest != '\t' && + *rest != ',' && *rest != ';' && + *rest != '-' && *rest != '+' && + *rest != '/' && + *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') + rest++; + /* skip over uninteresting chars. */ + SKIP_MORE: + while (*rest && + (*rest == ' ' || *rest == '\t' || + *rest == ',' || *rest == ';' || *rest == '/' || + *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) + rest++; + + /* "-" is ignored at the beginning of a token if we have not yet + parsed a year (e.g., the second "-" in "30-AUG-1966"), or if + the character after the dash is not a digit. */ + if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0) + || rest[1] < '0' || rest[1] > '9')) + { + rest++; + goto SKIP_MORE; + } + + } + + if (zone != TT_UNKNOWN && zone_offset == -1) + { + switch (zone) + { + case TT_PST: zone_offset = -8 * 60; break; + case TT_PDT: zone_offset = -7 * 60; break; + case TT_MST: zone_offset = -7 * 60; break; + case TT_MDT: zone_offset = -6 * 60; break; + case TT_CST: zone_offset = -6 * 60; break; + case TT_CDT: zone_offset = -5 * 60; break; + case TT_EST: zone_offset = -5 * 60; break; + case TT_EDT: zone_offset = -4 * 60; break; + case TT_AST: zone_offset = -4 * 60; break; + case TT_NST: zone_offset = -3 * 60 - 30; break; + case TT_GMT: zone_offset = 0 * 60; break; + case TT_BST: zone_offset = 1 * 60; break; + case TT_MET: zone_offset = 1 * 60; break; + case TT_EET: zone_offset = 2 * 60; break; + case TT_JST: zone_offset = 9 * 60; break; + default: + PR_ASSERT (0); + break; + } + } + + /* If we didn't find a year, month, or day-of-the-month, we can't + possibly parse this, and in fact, mktime() will do something random + (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt + a numerologically significant date... */ + if (month == TT_UNKNOWN || date == -1 || year == -1) + return PR_FAILURE; + + memset(&tm, 0, sizeof(tm)); + if (sec != -1) + tm.tm_sec = sec; + if (min != -1) + tm.tm_min = min; + if (hour != -1) + tm.tm_hour = hour; + if (date != -1) + tm.tm_mday = date; + if (month != TT_UNKNOWN) + tm.tm_month = (((int)month) - ((int)TT_JAN)); + if (year != -1) + tm.tm_year = year; + if (dotw != TT_UNKNOWN) + tm.tm_wday = (((int)dotw) - ((int)TT_SUN)); + + if (zone == TT_UNKNOWN && default_to_gmt) + { + /* No zone was specified, so pretend the zone was GMT. */ + zone = TT_GMT; + zone_offset = 0; + } + + if (zone_offset == -1) + { + /* no zone was specified, and we're to assume that everything + is local. */ + struct tm localTime; + time_t secs; + + PR_ASSERT(tm.tm_month > -1 + && tm.tm_mday > 0 + && tm.tm_hour > -1 + && tm.tm_min > -1 + && tm.tm_sec > -1); + + /* + * To obtain time_t from a tm structure representing the local + * time, we call mktime(). However, we need to see if we are + * on 1-Jan-1970 or before. If we are, we can't call mktime() + * because mktime() will crash on win16. In that case, we + * calculate zone_offset based on the zone offset at + * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the + * date we are parsing to transform the date to GMT. We also + * do so if mktime() returns (time_t) -1 (time out of range). + */ + + /* month, day, hours, mins and secs are always non-negative + so we dont need to worry about them. */ + if(tm.tm_year >= 1970) + { + PRInt64 usec_per_sec; + + localTime.tm_sec = tm.tm_sec; + localTime.tm_min = tm.tm_min; + localTime.tm_hour = tm.tm_hour; + localTime.tm_mday = tm.tm_mday; + localTime.tm_mon = tm.tm_month; + localTime.tm_year = tm.tm_year - 1900; + /* Set this to -1 to tell mktime "I don't care". If you set + it to 0 or 1, you are making assertions about whether the + date you are handing it is in daylight savings mode or not; + and if you're wrong, it will "fix" it for you. */ + localTime.tm_isdst = -1; + secs = mktime(&localTime); + if (secs != (time_t) -1) + { + LL_I2L(*result, secs); + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_MUL(*result, *result, usec_per_sec); + return PR_SUCCESS; + } + } + + /* So mktime() can't handle this case. We assume the + zone_offset for the date we are parsing is the same as + the zone offset on 00:00:00 2 Jan 1970 GMT. */ + secs = 86400; + (void) MT_safe_localtime(&secs, &localTime); + zone_offset = localTime.tm_min + + 60 * localTime.tm_hour + + 1440 * (localTime.tm_mday - 2); + } + + /* Adjust the hours and minutes before handing them to + PR_ImplodeTime(). Note that it's ok for them to be <0 or >24/60 + + We adjust the time to GMT before going into PR_ImplodeTime(). + The zone_offset represents the difference between the time + zone parsed and GMT + */ + tm.tm_hour -= (zone_offset / 60); + tm.tm_min -= (zone_offset % 60); + + *result = PR_ImplodeTime(&tm); + + return PR_SUCCESS; +} + +/* + ******************************************************************* + ******************************************************************* + ** + ** OLD COMPATIBILITY FUNCTIONS + ** + ******************************************************************* + ******************************************************************* + */ + + +/* + *----------------------------------------------------------------------- + * + * PR_FormatTime -- + * + * Format a time value into a buffer. Same semantics as strftime(). + * + *----------------------------------------------------------------------- + */ + +PR_IMPLEMENT(PRUint32) +PR_FormatTime(char *buf, int buflen, char *fmt, const PRExplodedTime *tm) +{ + struct tm a; + a.tm_sec = tm->tm_sec; + a.tm_min = tm->tm_min; + a.tm_hour = tm->tm_hour; + a.tm_mday = tm->tm_mday; + a.tm_mon = tm->tm_month; + a.tm_wday = tm->tm_wday; + a.tm_year = tm->tm_year - 1900; + a.tm_yday = tm->tm_yday; + a.tm_isdst = tm->tm_params.tp_dst_offset ? 1 : 0; + +/* + * On SunOS 4, struct tm has two additional fields: tm_zone + * and tm_gmtoff. The following code attempts to obtain values for + * these two fields. + */ + +#if defined(SUNOS4) || defined(MACLINUX) || (__GLIBC__ >= 2) + if (mktime(&a) == -1) { + PR_snprintf(buf, buflen, "can't get timezone"); + return 0; + } +#endif + + return strftime(buf, buflen, fmt, &a); +} + + +/* + * The following string arrays and macros are used by PR_FormatTimeUSEnglish(). + */ + +static const char* abbrevDays[] = +{ + "Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; + +static const char* days[] = +{ + "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" +}; + +static const char* abbrevMonths[] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +static const char* months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + + +/* + * Add a single character to the given buffer, incrementing the buffer pointer + * and decrementing the buffer size. Return 0 on error. + */ +#define ADDCHAR( buf, bufSize, ch ) \ +do \ +{ \ + if( bufSize < 1 ) \ + { \ + *(--buf) = '\0'; \ + return 0; \ + } \ + *buf++ = ch; \ + bufSize--; \ +} \ +while(0) + + +/* + * Add a string to the given buffer, incrementing the buffer pointer + * and decrementing the buffer size appropriately. Return 0 on error. + */ +#define ADDSTR( buf, bufSize, str ) \ +do \ +{ \ + PRUint32 strSize = strlen( str ); \ + if( strSize > bufSize ) \ + { \ + if( bufSize==0 ) \ + *(--buf) = '\0'; \ + else \ + *buf = '\0'; \ + return 0; \ + } \ + memcpy(buf, str, strSize); \ + buf += strSize; \ + bufSize -= strSize; \ +} \ +while(0) + +/* Needed by PR_FormatTimeUSEnglish() */ +static unsigned int pr_WeekOfYear(const PRExplodedTime* time, + unsigned int firstDayOfWeek); + + +/*********************************************************************************** + * + * Description: + * This is a dumbed down version of strftime that will format the date in US + * English regardless of the setting of the global locale. This functionality is + * needed to write things like MIME headers which must always be in US English. + * + **********************************************************************************/ + +PR_IMPLEMENT(PRUint32) +PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize, + const char* format, const PRExplodedTime* time ) +{ + char* bufPtr = buf; + const char* fmtPtr; + char tmpBuf[ 40 ]; + const int tmpBufSize = sizeof( tmpBuf ); + + + for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ ) + { + if( *fmtPtr != '%' ) + { + ADDCHAR( bufPtr, bufSize, *fmtPtr ); + } + else + { + switch( *(++fmtPtr) ) + { + case '%': + /* escaped '%' character */ + ADDCHAR( bufPtr, bufSize, '%' ); + break; + + case 'a': + /* abbreviated weekday name */ + ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] ); + break; + + case 'A': + /* full weekday name */ + ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] ); + break; + + case 'b': + /* abbreviated month name */ + ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] ); + break; + + case 'B': + /* full month name */ + ADDSTR(bufPtr, bufSize, months[ time->tm_month ] ); + break; + + case 'c': + /* Date and time. */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'd': + /* day of month ( 01 - 31 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'H': + /* hour ( 00 - 23 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'I': + /* hour ( 01 - 12 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld", + (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'j': + /* day number of year ( 001 - 366 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'm': + /* month number ( 01 - 12 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'M': + /* minute ( 00 - 59 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'p': + /* locale's equivalent of either AM or PM */ + ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" ); + break; + + case 'S': + /* seconds ( 00 - 61 ), allows for leap seconds */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'U': + /* week number of year ( 00 - 53 ), Sunday is the first day of week 1 */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'w': + /* weekday number ( 0 - 6 ), Sunday = 0 */ + PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'W': + /* Week number of year ( 00 - 53 ), Monday is the first day of week 1 */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'x': + /* Date representation */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'X': + /* Time representation. */ + PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'y': + /* year within century ( 00 - 99 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'Y': + /* year as ccyy ( for example 1986 ) */ + PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + case 'Z': + /* Time zone name or no characters if no time zone exists. + * Since time zone name is supposed to be independant of locale, we + * defer to PR_FormatTime() for this option. + */ + PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time ); + ADDSTR( bufPtr, bufSize, tmpBuf ); + break; + + default: + /* Unknown format. Simply copy format into output buffer. */ + ADDCHAR( bufPtr, bufSize, '%' ); + ADDCHAR( bufPtr, bufSize, *fmtPtr ); + break; + + } + } + } + + ADDCHAR( bufPtr, bufSize, '\0' ); + return (PRUint32)(bufPtr - buf - 1); +} + + + +/*********************************************************************************** + * + * Description: + * Returns the week number of the year (0-53) for the given time. firstDayOfWeek + * is the day on which the week is considered to start (0=Sun, 1=Mon, ...). + * Week 1 starts the first time firstDayOfWeek occurs in the year. In other words, + * a partial week at the start of the year is considered week 0. + * + **********************************************************************************/ + +static unsigned int +pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek) +{ + int dayOfWeek; + int dayOfYear; + + /* Get the day of the year for the given time then adjust it to represent the + * first day of the week containing the given time. + */ + dayOfWeek = time->tm_wday - firstDayOfWeek; + if (dayOfWeek < 0) + dayOfWeek += 7; + + dayOfYear = time->tm_yday - dayOfWeek; + + + if( dayOfYear <= 0 ) + { + /* If dayOfYear is <= 0, it is in the first partial week of the year. */ + return 0; + } + else + { + /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there + * are any days left over ( dayOfYear % 7 ). Because we are only counting to + * the first day of the week containing the given time, rather than to the + * actual day representing the given time, any days in week 0 will be "absorbed" + * as extra days in the given week. + */ + return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 ); + } +} + diff --git a/pr/src/nspr.rc b/pr/src/nspr.rc new file mode 100644 index 00000000..1b9b146c --- /dev/null +++ b/pr/src/nspr.rc @@ -0,0 +1,126 @@ +/* -*- 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 "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef WIN16 +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L + +// end win16 +#endif + +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Netscape Communications Corporation\0" + VALUE "FileDescription", "Netscape Portable Run Time\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "nspr20\0" + VALUE "LegalCopyright", "Copyright © 1996\0" + VALUE "OriginalFilename", "nspr20.dll\0" + VALUE "ProductName", "Netscape Communication Corporation NSPR20\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/pr/src/pthreads/Makefile b/pr/src/pthreads/Makefile new file mode 100644 index 00000000..1a21d806 --- /dev/null +++ b/pr/src/pthreads/Makefile @@ -0,0 +1,47 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +CSRCS = \ + ptio.c \ + ptsynch.c \ + ptthread.c \ + ptmisc.c \ + $(NULL) + +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/pthreads/ptio.c b/pr/src/pthreads/ptio.c new file mode 100644 index 00000000..171877e7 --- /dev/null +++ b/pr/src/pthreads/ptio.c @@ -0,0 +1,3381 @@ +/* -*- 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. + */ + +/* +** File: ptio.c +** Descritpion: Implemenation of I/O methods for pthreads +** Exports: ptio.h +*/ + +#if defined(_PR_PTHREADS) + +#include <string.h> /* for memset() */ +#include <sys/types.h> +#include <dirent.h> + +/* + * XXX: On Linux 2.0.27 sched.h uses this _P macro that seems to be undefined. + * I suspect that it is a typo (should be __P). + */ +#if defined(LINUX) +#define _P(x) __P(x) +#endif +#include <pthread.h> + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#ifdef SOLARIS +#include <sys/filio.h> /* to pick up FIONREAD */ +#endif +/* Linux (except glibc) and FreeBSD don't have poll */ +#if !(defined(LINUX) && !(defined(__GLIBC__) && __GLIBC__ >= 2)) \ + && !defined(FREEBSD) +#include <poll.h> +#endif +#ifdef AIX +/* To pick up sysconf() */ +#include <unistd.h> +#else +/* To pick up getrlimit() etc. */ +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#include "primpl.h" + +/* On Alpha Linux, these are already defined in sys/socket.h */ +#if !(defined(LINUX) && defined(__alpha)) +#include <netinet/tcp.h> /* TCP_NODELAY, TCP_MAXSEG */ +#endif + +#if defined(SOLARIS) +#define _PRSockOptVal_t char * +#elif defined(IRIX) || defined(OSF1) || defined(AIX) || defined(HPUX) \ + || defined(LINUX) || defined(FREEBSD) +#define _PRSockOptVal_t void * +#else +#error "Cannot determine architecture" +#endif + +#if (defined(HPUX) && !defined(HPUX10_30) && !defined(HPUX11)) +#define _PRSelectFdSetArg_t int * +#elif defined(AIX4_1) +#define _PRSelectFdSetArg_t void * +#elif defined(IRIX) || (defined(AIX) && !defined(AIX4_1)) \ + || defined(OSF1) || defined(SOLARIS) \ + || defined(HPUX10_30) || defined(HPUX11) || defined(LINUX) \ + || defined(FREEBSD) +#define _PRSelectFdSetArg_t fd_set * +#else +#error "Cannot determine architecture" +#endif + +static PRStatus pt_InitIOContinuation(void); +static PRFileDesc *pt_SetMethods(PRIntn osfd, PRDescType type); + +static pthread_condattr_t _pt_cvar_attr; +static PRLock *_pr_flock_lock; /* For PR_LockFile() etc. */ +static PRLock *_pr_rename_lock; /* For PR_Rename() */ + +extern struct _PT_Bookeeping pt_book; /* defined in ptthread.c */ +extern PRIntn pt_schedpriv; /* defined in ptthread.c */ + +/*****************************************************************************/ +/*****************************************************************************/ +/************************** File descriptor caching **************************/ +/*****************************************************************************/ +/*****************************************************************************/ + +typedef struct _PT_Fd_Cache +{ + PRLock *ml; + PRIntn count; + PRIntn limit; + PRFileDesc *fd; +} _PT_Fd_Cache; +static _PT_Fd_Cache pt_fd_cache; + +/* +** Get a FileDescriptor from the cache if one exists. If not allocate +** a new one from the heap. +*/ +static PRFileDesc *pt_Getfd(void) +{ + PRFileDesc *fd; + do + { + fd = pt_fd_cache.fd; /* quick, unsafe check */ + if (NULL == fd) + { + fd = PR_NEWZAP(PRFileDesc); + if (NULL == fd) goto finished; + fd->secret = PR_NEWZAP(PRFilePrivate); + if (NULL == fd->secret) + { + PR_DELETE(fd); + goto finished; + } + } + else + { + PRFilePrivate *secret; + PR_Lock(pt_fd_cache.ml); + fd = pt_fd_cache.fd; /* safer extraction */ + if (NULL != fd) + { + pt_fd_cache.count -= 1; + pt_fd_cache.fd = fd->higher; + fd->higher = NULL; + } + PR_Unlock(pt_fd_cache.ml); + secret = fd->secret; + memset(fd, 0, sizeof(PRFileDesc)); + memset(secret, 0, sizeof(PRFilePrivate)); + fd->secret = secret; + } + } while (NULL == fd); +finished: + return fd; +} /* pt_Getfd */ + +/* +** Return a file descriptor to the cache unless there are too many in +** there already. If put in cache, clear the fields first. +*/ +static void pt_Putfd(PRFileDesc *fd) +{ + PR_ASSERT(_PR_FILEDESC_CLOSED == fd->secret->state); + PR_ASSERT(pt_fd_cache.count < pt_fd_cache.limit); + + fd->secret->state = _PR_FILEDESC_FREED; + if (pt_fd_cache.count > pt_fd_cache.limit) + { + PR_DELETE(fd->secret); + PR_DELETE(fd); + } + else + { + PR_Lock(pt_fd_cache.ml); + pt_fd_cache.count += 1; + fd->higher = pt_fd_cache.fd; + pt_fd_cache.fd = fd; + PR_Unlock(pt_fd_cache.ml); + } +} /* pt_Putfd */ + +/*****************************************************************************/ +/************************* I/O Continuation machinery ************************/ +/*****************************************************************************/ + +/* + * The polling interval defines the maximum amount of time that a thread + * might hang up before an interrupt is noticed. + */ +#define PT_DEFAULT_POLL_MSEC 100 + +/* + * Latest POSIX defines this type as socklen_t. It may also be + * size_t or int. + */ +#if (defined(LINUX) && defined(__GLIBC__) && __GLIBC__ >= 2 \ + && !defined(__alpha)) +typedef socklen_t pt_SockLen; +#elif defined(AIX) || (defined(LINUX) && defined(__alpha)) +typedef PRSize pt_SockLen; +#else +typedef PRIntn pt_SockLen; +#endif + +typedef struct pt_Continuation pt_Continuation; +typedef PRBool (*ContinuationFn)(pt_Continuation *op, PRInt16 revents); + +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 { PRIntn osfd; } arg1; /* #1 - the op's fd */ + union { void* buffer; } arg2; /* #2 - primary transfer buffer */ + union { + PRSize amount; /* #3 - size of 'buffer', or */ + pt_SockLen *addr_len; /* - length of address */ +#ifdef HPUX11 + /* + * For sendfile() + */ + off_t offset; /* offset in file to send */ +#endif + } arg3; + union { PRIntn flags; } arg4; /* #4 - read/write flags */ + union { PRNetAddr *addr; } arg5; /* #5 - send/recv address */ + +#ifdef HPUX11 + /* + * For sendfile() + */ + int filedesc; /* descriptor of file to send */ + int nbytes_to_send; /* size of header and file */ +#endif /* HPUX11 */ + + PRIntervalTime timeout; /* client (relative) timeout */ + PRIntervalTime absolute; /* internal (absolute) timeout */ + + PRInt16 event; /* flags for poll()'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 { PRSize 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 +{ + PRCallOnceType once; /* controls the initialization + * of this structure */ + PRLock *ml; /* a little protection */ + PRThread *thread; /* internal thread's identification */ + PRCondVar *new_op; /* new operation supplied */ + 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 */ + PRBool exitFlag; /* a Boolean flag for signaling the + * continuation thread to exit */ +} pt_tq; + +#if defined(DEBUG) +static struct pt_debug_s +{ + PRUintn predictionsFoiled; + PRUintn pollingListMax; + PRUintn continuationsServed; +} pt_debug; +#endif /* DEBUG */ + +/* + * The following two functions, pt_InsertTimedInternal and + * pt_FinishTimedInternal, are always called with the pt_tq.ml + * lock held. The "internal" in the functions' names come from + * the Mesa programming language. Internal functions are always + * called from inside a monitor. + */ + +static void pt_InsertTimedInternal(pt_Continuation *op) +{ + pt_Continuation *t_op = NULL; + PRIntervalTime now = PR_IntervalNow(); + + /* + * 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 portion of this routine deals with timed ops. + */ + op->absolute = now + op->timeout; /* absolute ticks */ + if (NULL == pt_tq.op) pt_tq.op = op; + else + { + /* + * To find where in the list to put the new operation, based + * on the absolute time the operation in question will expire. + * + * The new operation ('op') will expire at now() + op->timeout. + * + * This should be easy! + */ + + for (t_op = pt_tq.op; NULL != t_op; t_op = t_op->prev) + { + /* + * 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 computed. + */ + if ((PRInt32)(op->absolute - t_op->absolute) >= 0) + { + if (t_op == pt_tq.op) pt_tq.op = op; + break; + } + } + } + +done: + + /* + * Insert 'op' into the queue just after t_op or if t_op is null, + * at the head of the list. + * + * We need to set up the 'next' and 'prev' pointers of 'op' + * correctly before inserting 'op' into the queue. Also, we insert + * 'op' by updating pt_tq.head or op->prev->next first, and then + * updating op->next->prev. We want to make sure that the 'next' + * pointers are linked up correctly at all times so that we can + * traverse the queue by starting with pt_tq.head and following + * the 'next' pointers, without having to acquire the pt_tq.ml lock. + * (We do that in ContinuationThread.) We traverse the 'prev' + * pointers only in this function, which is called with the lock held. + * + * Similar care is taken in pt_FinishTimedInternal where we remove + * an op from the queue. + */ + 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; + } + + pt_tq.op_count += 1; + +} /* pt_InsertTimedInternal */ + +/* + * function: pt_FinishTimedInternal + * + * 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 */ + PRInt32 msecs, mx_poll_ticks; + struct pollfd *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 */ + + mx_poll_ticks = (PRInt32)PR_MillisecondsToInterval(PT_DEFAULT_POLL_MSEC); + + /* do some real work */ + while (PR_TRUE) + { + PRIntn rv; + PRInt32 timeout; + PRIntn pollIndex; + PRIntervalTime now; + pt_Continuation *op; + + PR_Lock(pt_tq.ml); + while (!pt_tq.exitFlag && (NULL == pt_tq.head)) + PR_WaitCondVar(pt_tq.new_op, PR_INTERVAL_NO_TIMEOUT); + pollingListNeeded = pt_tq.op_count; + PR_Unlock(pt_tq.ml); + + /* Okay. We're history */ + if (pt_tq.exitFlag) 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. + */ + pollingListNeeded += 10; + if (pollingListNeeded > pollingSlotsAllocated) + { + if (NULL != pollingList) PR_DELETE(pollingList); + pollingList = (struct pollfd*)PR_Malloc( + pollingListNeeded * sizeof(struct pollfd)); + PR_ASSERT(NULL != pollingList); + pollingSlotsAllocated = pollingListNeeded; + } + +#if defined(DEBUG) + if (pollingListNeeded > pt_debug.pollingListMax) + pt_debug.pollingListMax = pollingListNeeded; +#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 = EINTR; + op = pt_FinishTimedInternal(op); + } + else + { + if (pollingListUsed == pollingSlotsAllocated) break; + PR_ASSERT(pt_continuation_done != op->status); + op->status = pt_continuation_inprogress; + pollingList[pollingListUsed].revents = 0; + pollingList[pollingListUsed].fd = op->arg1.osfd; + pollingList[pollingListUsed].events = op->event; + pollingListUsed += 1; + 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 */ + } + + if (NULL == pt_tq.head) continue; /* did list evaporate? */ + + /* + * 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 (PR_INTERVAL_NO_TIMEOUT == pt_tq.head->timeout) + msecs = PT_DEFAULT_POLL_MSEC; + else + { + timeout = pt_tq.head->absolute - PR_IntervalNow(); + if (timeout <= 0) msecs = 0; /* already timed out */ + else if (timeout >= mx_poll_ticks) msecs = PT_DEFAULT_POLL_MSEC; + else msecs = (PRInt32)PR_IntervalToMilliseconds(timeout); + } + + rv = poll(pollingList, pollingListUsed, msecs); + + if ((-1 == rv) && ((errno == EINTR) || (errno == EAGAIN))) + continue; /* go around the loop again */ + + if (rv > 0) + { + /* + * poll() says that something in our list is ready for some more + * action. Find it, load up the operation and see what happens. + */ + + /* + * 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) + { + PR_ASSERT(NULL != op); + if (0 != pollingList[pollIndex].revents) + { + /* + * 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 operate on. + */ + while ((pollingList[pollIndex].fd != op->arg1.osfd) + || (pollingList[pollIndex].events != op->event)) + { + PR_ASSERT(NULL != op->next); /* it has to be in there */ + op = op->next; /* keep advancing down the list */ + } + + /* + * Skip 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 ((pt_continuation_inprogress == op->status) + && (op->function(op, pollingList[pollIndex].revents))) + { + PR_Lock(pt_tq.ml); + op = pt_FinishTimedInternal(op); + PR_Unlock(pt_tq.ml); + } + continue; + } + op = op->next; /* progress to next operation */ + } + } + + /* + * This is timeout processing. It is done after checking + * for good completions. Those that just made it under the + * wire are lucky, but none the less, valid. + */ + if ((NULL != pt_tq.head) + && (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)) + { + now = PR_IntervalNow(); + while ((PRInt32)(pt_tq.head->absolute - now) <= 0) + { + /* + * 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); + pt_tq.head->result.code = -1; + pt_tq.head->syserrno = ETIMEDOUT; + (void)pt_FinishTimedInternal(pt_tq.head); + PR_Unlock(pt_tq.ml); + if ((NULL == pt_tq.head) + || (PR_INTERVAL_NO_TIMEOUT == pt_tq.head->timeout)) + break; + } + } + } + if (NULL != pollingList) PR_DELETE(pollingList); +} /* ContinuationThread */ + +static PRIntn pt_Continue(pt_Continuation *op) +{ + PRIntn rc; + PRStatus rv; +#ifdef DEBUG + PRBool waitcv_interrupted = PR_FALSE; +#endif /* DEBUG */ + + PR_CallOnce(&pt_tq.once, pt_InitIOContinuation); + + /* Finish filling in the blank slots */ + /* op->complete = PR_NewCondVar(pt_tq.ml); */ + op->complete.lock = pt_tq.ml; + rc = PTHREAD_COND_INIT(op->complete.cv, _pt_cvar_attr); PR_ASSERT(0 == rc); + op->status = pt_continuation_sumbitted; + 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 + * irritate him. He's cycling around at least every mx_poll_ticks + * anyhow and should notice the request in there. + */ + if ((PR_FAILURE == rv) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) + { +#ifdef DEBUG + waitcv_interrupted = PR_TRUE; +#endif /* DEBUG */ + if (pt_continuation_done != op->status) + { + /* tell the continuation thread to abort the operation */ + op->status = pt_continuation_abort; + } + else + { + op->result.code = -1; + op->syserrno = EINTR; + } + } + } + + PR_Unlock(pt_tq.ml); /* we provide the locking */ + rc = pthread_cond_destroy(&op->complete.cv); PR_ASSERT(0 == rc); + + /* make sure that the continuation thread did abort the operation */ +#ifdef DEBUG + if (PR_TRUE == waitcv_interrupted) + { + PR_ASSERT(-1 == op->result.code); + PR_ASSERT(EINTR == op->syserrno); + } +#endif /* DEBUG */ + return op->result.code; /* and the primary answer */ +} /* pt_Continue */ + +/*****************************************************************************/ +/*********************** specific continuation functions *********************/ +/*****************************************************************************/ +static PRBool pt_connect_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = _MD_unix_get_nonblocking_connect_error(op->arg1.osfd); + if (op->syserrno != 0) { + op->result.code = -1; + } else { + op->result.code = 0; + } + return PR_TRUE; /* this one is cooked */ +} /* pt_connect_cont */ + +static PRBool pt_accept_cont(pt_Continuation *op, PRInt16 revents) +{ + op->syserrno = 0; + op->result.code = accept( + op->arg1.osfd, op->arg2.buffer, op->arg3.addr_len); + if (-1 == op->result.code) + { + op->syserrno = errno; + if (EWOULDBLOCK == errno || EAGAIN == errno) /* the only thing we allow */ + return PR_FALSE; /* do nothing - this one ain't finished */ + } + return PR_TRUE; +} /* pt_accept_cont */ + +static PRBool pt_read_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ + op->result.code = read( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_read_cont */ + +static PRBool pt_recv_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Any number of bytes will complete the operation. It need + * not (and probably will not) satisfy the request. The only + * error we continue is EWOULDBLOCK|EAGAIN. + */ + op->result.code = recv( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); + op->syserrno = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recv_cont */ + +static PRBool pt_send_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ + PRIntn bytes = send( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount, op->arg4.flags); + op->syserrno = errno; + if (bytes > 0) /* this is progress */ + { + char *bp = (char*)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) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_send_cont */ + +static PRBool pt_write_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * We want to write the entire amount out, no matter how many + * tries it takes. Keep advancing the buffer and the decrementing + * the amount until the amount goes away. Return the total bytes + * (which should be the original amount) when finished (or an + * error). + */ + PRIntn bytes = write( + op->arg1.osfd, op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + if (bytes > 0) /* this is progress */ + { + char *bp = (char*)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) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_write_cont */ + +static PRBool pt_writev_cont(pt_Continuation *op, PRInt16 revents) +{ + /* + * Same rules as write, but continuing seems to be a bit more + * complicated. As the number of bytes sent grows, we have to + * redefine the vector we're pointing at. We might have to + * modify an individual vector parms or we might have to eliminate + * a pair altogether. + */ + PRIntn bytes = writev( + op->arg1.osfd, (struct iovec*)op->arg2.buffer, op->arg3.amount); + op->syserrno = errno; + if (bytes > 0) /* this is progress */ + { + PRIntn iov_index; + struct iovec *iov = (struct iovec*)op->arg2.buffer; + op->result.code += bytes; /* accumulate the number sent */ + for (iov_index = 0; iov_index < op->arg3.amount; ++iov_index) + { + /* how much progress did we make in the i/o vector? */ + if (bytes < iov[iov_index].iov_len) + { + /* this element's not done yet */ + char **bp = (char**)&(iov[iov_index].iov_base); + iov[iov_index].iov_len -= bytes; /* there's that much left */ + *bp += bytes; /* starting there */ + break; /* go off and do that */ + } + bytes -= iov[iov_index].iov_len; /* that element's consumed */ + } + op->arg2.buffer = &iov[iov_index]; /* new start of array */ + op->arg3.amount -= iov_index; /* and array length */ + return (0 == op->arg3.amount) ? PR_TRUE : PR_FALSE; + } + else return ((-1 == bytes) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_writev_cont */ + +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, PR_NETADDR_SIZE(op->arg5.addr)); + op->syserrno = errno; + if (bytes > 0) /* this is progress */ + { + char *bp = (char*)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) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_sendto_cont */ + +static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) +{ + pt_SockLen addr_len = sizeof(PRNetAddr); + 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 = errno; + return ((-1 == op->result.code) && + (EWOULDBLOCK == op->syserrno || EAGAIN == op->syserrno)) ? + PR_FALSE : PR_TRUE; +} /* pt_recvfrom_cont */ + +#ifdef HPUX11 +static PRBool pt_hpux_transmitfile_cont(pt_Continuation *op, PRInt16 revents) +{ + struct iovec *hdtrl = (struct iovec *) op->arg2.buffer; + int count; + + count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.offset, 0, + hdtrl, op->arg4.flags); + PR_ASSERT(count <= op->nbytes_to_send); + + if (count != -1) { + op->result.code += count; + } else if (errno != EWOULDBLOCK && errno != EAGAIN) { + op->result.code = -1; + } else { + return PR_FALSE; + } + + if (count != -1 && count < op->nbytes_to_send) { + if (hdtrl[0].iov_len == 0) { + PR_ASSERT(hdtrl[0].iov_base == NULL); + op->arg3.offset += count; + } else if (count < hdtrl[0].iov_len) { + PR_ASSERT(op->arg3.offset == 0); + hdtrl[0].iov_base = (char *) hdtrl[0].iov_base + count; + hdtrl[0].iov_len -= count; + } else { + op->arg3.offset = count - hdtrl[0].iov_len; + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + } + op->nbytes_to_send -= count; + return PR_FALSE; + } + + return PR_TRUE; +} +#endif /* HPUX11 */ + +#if !defined(PT_NO_ATFORK) + +static void pt_BeforeFork() +{ + PRStatus rv; + PRThread *thred = pt_tq.thread; + + /* + * We shut down the continuation thread cleanly only if there are + * no other threads in the process when fork() is called. + * If there are other threads, they won't be duplicated in the child + * process. Then the child process may already be unclean, so there + * is no point for us to try to be clean. + */ + + if ((NULL != thred) && (2 == pt_book.user + pt_book.system)) + { + /* pt_tq should be empty */ + PR_ASSERT((0 == pt_tq.op_count) + && (NULL == pt_tq.head) + && (NULL == pt_tq.tail) + && (NULL == pt_tq.op)); + pt_tq.exitFlag = PR_TRUE; + rv = PR_Interrupt(thred); + PR_ASSERT(PR_SUCCESS == rv); + rv = PR_JoinThread(thred); + PR_ASSERT(PR_SUCCESS == rv); + pt_tq.exitFlag = PR_FALSE; + /* Indicates that the continuation thread is shut down cleanly */ + pt_tq.thread = NULL; + memset(&pt_tq.once, 0, sizeof(pt_tq.once)); + PR_ASSERT(1 == pt_book.user + pt_book.system); + } +} /* pt_BeforeFork */ + +static void pt_AfterForkParent(void) +{ +} /* pt_AfterForkParent */ + +static void pt_AfterForkChild(void) +{ + /* + * pt_tq may be in a corrupted state if the continuation thread + * existed and was not terminated cleanly before fork. In this + * case, we expect the child process to call exec immediately. + */ +} /* pt_AfterForkChild */ + +#endif /* PT_NO_ATFORK */ + +void _PR_InitIO() +{ + _pr_stdin = pt_SetMethods(0, PR_DESC_FILE); + _pr_stdout = pt_SetMethods(1, PR_DESC_FILE); + _pr_stderr = pt_SetMethods(2, PR_DESC_FILE); + + PR_ASSERT(_pr_stdin && _pr_stdout && _pr_stderr); + + _pr_flock_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_flock_lock); + _pr_rename_lock = PR_NewLock(); + PR_ASSERT(NULL != _pr_rename_lock); + + pt_fd_cache.ml = PR_NewLock(); + PR_ASSERT(NULL != pt_fd_cache.ml); + pt_fd_cache.limit = FD_SETSIZE; +} /* _PR_InitIO */ + +static PRStatus pt_InitIOContinuation() +{ + PRIntn rv; + + PR_ASSERT((0 == pt_tq.op_count) + && (NULL == pt_tq.head) + && (NULL == pt_tq.tail) + && (NULL == pt_tq.op) + && (PR_FALSE == pt_tq.exitFlag)); + + if (NULL == pt_tq.ml) + { + /* The very first time */ + 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); + rv = PTHREAD_CONDATTR_INIT(&_pt_cvar_attr); + PR_ASSERT(0 == rv); + +#if !defined(PT_NO_ATFORK) + rv = pthread_atfork(pt_BeforeFork, + pt_AfterForkParent, pt_AfterForkChild); + PR_ASSERT(0 == rv); +#endif + } + + pt_tq.thread = PR_CreateThread( + PR_SYSTEM_THREAD, ContinuationThread, NULL, + PR_PRIORITY_URGENT, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0); + PR_ASSERT(NULL != pt_tq.thread); + + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRFileDesc*) PR_GetSpecialFD(PRSpecialFD osfd) +{ + PRFileDesc *result = NULL; + PR_ASSERT(osfd >= PR_StandardInput && osfd <= PR_StandardError); + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + switch (osfd) + { + case PR_StandardInput: result = _pr_stdin; break; + case PR_StandardOutput: result = _pr_stdout; break; + case PR_StandardError: result = _pr_stderr; break; + default: + (void)PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + } + return result; +} /* PR_GetSpecialFD */ + +/*****************************************************************************/ +/***************************** I/O private methods ***************************/ +/*****************************************************************************/ + +static PRBool pt_TestAbort(void) +{ + PRThread *me = PR_CurrentThread(); + if(me->state & PT_THREAD_ABORTED) + { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + me->state &= ~PT_THREAD_ABORTED; + return PR_TRUE; + } + return PR_FALSE; +} /* pt_TestAbort */ + +static void pt_MapError(void (*mapper)(PRIntn), PRIntn syserrno) +{ + switch (syserrno) + { + case EINTR: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); break; + case ETIMEDOUT: + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); break; + default: + mapper(syserrno); + } +} /* pt_MapError */ + +static PRStatus pt_Close(PRFileDesc *fd) +{ + PRIntn syserrno, rv = 0; + if ((NULL == fd) || (NULL == fd->secret)) + { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if (pt_TestAbort()) return PR_FAILURE; + + if (_PR_FILEDESC_OPEN == fd->secret->state) + { + fd->secret->state = _PR_FILEDESC_CLOSED; + rv = close(fd->secret->md.osfd); + syserrno = errno; + } + + pt_Putfd(fd); + if (-1 == rv) + { + pt_MapError(_PR_MD_MAP_CLOSE_ERROR, syserrno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Close */ + +static PRInt32 pt_Read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + + if (pt_TestAbort()) return PR_FAILURE; + + bytes = read(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.function = pt_read_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) + pt_MapError(_PR_MD_MAP_READ_ERROR, syserrno); + return bytes; +} /* pt_Read */ + +static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + + if (pt_TestAbort()) return PR_FAILURE; + + bytes = write(fd->secret->md.osfd, buf, amount); + syserrno = errno; + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.timeout = PR_INTERVAL_NO_TIMEOUT; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_write_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) + pt_MapError(_PR_MD_MAP_WRITE_ERROR, syserrno); + return bytes; +} /* pt_Write */ + +static PRInt32 pt_Writev( + PRFileDesc *fd, PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout) +{ + PRIntn iov_index = 0; + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes = -1, rv = -1; + + if (pt_TestAbort()) return rv; + + /* + * The first shot at this can use the client's iov directly. + * Only if we have to continue the operation do we have to + * make a copy that we can modify. + */ + rv = bytes = writev(fd->secret->md.osfd, (struct iovec*)iov, iov_len); + syserrno = errno; + + /* + * If we moved some bytes (ie., bytes > 0) how does that implicate + * the i/o vector list. In other words, exactly where are we within + * that array? What are the parameters for resumption? Maybe we're + * done! + */ + if ((bytes > 0) && (!fd->secret->nonblocking)) + { + for (iov_index = 0; iov_index < iov_len; ++iov_index) + { + if (bytes < iov[iov_index].iov_len) break; /* continue w/ what's left */ + bytes -= iov[iov_index].iov_len; /* this one's done cooked */ + } + } + + if ((bytes >= 0) && (iov_index < iov_len) && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + rv = -1; + syserrno = ETIMEDOUT; + } + else fNeedContinue = PR_TRUE; + } + else if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + rv = bytes = 0; + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + /* + * Okay. Now we need a modifiable copy of the array. + * Allocate some storage and copy the (already) modified + * bits into the new vector. The is copying only the + * part of the array that's still valid. The array may + * have a new length and the first element of the array may + * have different values. + */ + struct iovec *osiov = NULL, *tiov; + PRIntn osiov_len = iov_len - iov_index; /* recompute */ + osiov = (struct iovec*)PR_Malloc(osiov_len * sizeof(struct iovec)); + PR_ASSERT(NULL != osiov); + for (tiov = osiov; iov_index < iov_len; ++iov_index) + { + tiov->iov_base = iov[iov_index].iov_base; + tiov->iov_len = iov[iov_index].iov_len; + tiov += 1; + } + osiov->iov_len -= bytes; /* that may be partially done */ + /* so advance the description */ + osiov->iov_base = (char*)osiov->iov_base + bytes; + + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)osiov; + op.arg3.amount = osiov_len; + op.timeout = timeout; + op.result.code = rv; + op.function = pt_writev_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + PR_DELETE(osiov); + } + if (rv == -1) pt_MapError(_PR_MD_MAP_WRITEV_ERROR, syserrno); + return rv; +} /* pt_Writev */ + +static PRInt32 pt_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) +{ + PRIntn how; + off_t pos = -1; + + if (pt_TestAbort()) return pos; + + switch (whence) + { + case PR_SEEK_SET: how = SEEK_SET; break; + case PR_SEEK_CUR: how = SEEK_CUR; break; + case PR_SEEK_END: how = SEEK_END; break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return pos; + } + pos = lseek(fd->secret->md.osfd, offset, how); + if (pos == -1) + pt_MapError(_PR_MD_MAP_LSEEK_ERROR, errno); + return pos; +} /* pt_Seek */ + +static PRInt64 pt_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) +{ + PRInt64 on; + PRInt32 off, position = -1; + LL_L2I(off, offset); /* possible loss of bits */ + LL_I2L(on, off); /* get back original or notice we didn't */ + if (LL_EQ(on, offset)) position = pt_Seek(fd, off, whence); + LL_I2L(on, position); /* might not have worked */ + return on; +} /* pt_Seek64 */ + +static PRInt32 pt_Available(PRFileDesc *fd) +{ + PRInt32 rv, bytes = -1; + if (pt_TestAbort()) return bytes; + + rv = ioctl(fd->secret->md.osfd, FIONREAD, &bytes); + + if (rv == -1) + pt_MapError(_PR_MD_MAP_SOCKETAVAILABLE_ERROR, errno); + return bytes; +} /* pt_Available */ + +static PRInt64 pt_Available64(PRFileDesc *fd) +{ + PRInt64 rv; + PRInt32 avail = pt_Available(fd); + LL_I2L(rv, avail); + return rv; +} /* pt_Available64 */ + +static PRStatus pt_Synch(PRFileDesc *fd) +{ + return (NULL == fd) ? PR_FAILURE : PR_SUCCESS; +} /* pt_Synch */ + +static PRStatus pt_FileInfo(PRFileDesc *fd, PRFileInfo *info) +{ + PRInt32 rv; + struct stat sb; + PRInt64 s, s2us; + + if ((rv = fstat(fd->secret->md.osfd, &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; +#if defined(IRIX) && defined(HAVE_LONG_LONG) + info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtim.tv_sec); + info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctim.tv_sec); + info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtime); + info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctime); +#else + LL_I2L(s, sb.st_mtime); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; +#endif + } + } + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* pt_FileInfo */ + +static PRStatus pt_FileInfo64(PRFileDesc *fd, PRFileInfo64 *info) +{ + PRFileInfo info32; + PRStatus rv = pt_FileInfo(fd, &info32); + if (PR_SUCCESS == rv) + { + info->type = info32.type; + info->creationTime = info32.creationTime; + info->modifyTime = info32.modifyTime; + LL_I2L(info->size, info32.size); + } + return rv; +} /* pt_FileInfo64 */ + +static PRStatus pt_Fsync(PRFileDesc *fd) +{ + PRIntn rv = -1; + if (pt_TestAbort()) return PR_FAILURE; + + rv = fsync(fd->secret->md.osfd); + if (rv < 0) { + pt_MapError(_PR_MD_MAP_FSYNC_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Fsync */ + +static PRStatus pt_Connect( + PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) +{ + PRIntn rv = -1, syserrno; + PRSize addr_len = PR_NETADDR_SIZE(addr); + + if (pt_TestAbort()) return PR_FAILURE; + + rv = connect(fd->secret->md.osfd, (struct sockaddr*)addr, addr_len); + syserrno = errno; + if ((-1 == rv) && (EINPROGRESS == syserrno) && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)addr; + op.arg3.amount = addr_len; + op.timeout = timeout; + op.function = pt_connect_cont; + op.event = POLLOUT | POLLPRI; + rv = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (-1 == rv) { + pt_MapError(_PR_MD_MAP_CONNECT_ERROR, syserrno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Connect */ + +PR_IMPLEMENT(PRStatus) PR_GetConnectStatus(const PRPollDesc *pd) +{ + PRInt32 osfd; + PRFileDesc *bottom = pd->fd; + int err; + + if (pd->out_flags & PR_POLL_NVAL) { + PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0); + return PR_FAILURE; + } + if ((pd->out_flags & (PR_POLL_WRITE | PR_POLL_EXCEPT | PR_POLL_ERR)) == 0) { + PR_ASSERT(pd->out_flags == 0); + PR_SetError(PR_IN_PROGRESS_ERROR, 0); + return PR_FAILURE; + } + + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + osfd = bottom->secret->md.osfd; + + err = _MD_unix_get_nonblocking_connect_error(osfd); + if (err != 0) { + _PR_MD_MAP_CONNECT_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +static PRFileDesc* pt_Accept( + PRFileDesc *fd, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRFileDesc *newfd = NULL; + PRIntn syserrno, osfd = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) return newfd; + + osfd = accept(fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if (osfd == -1) + { + if (fd->secret->nonblocking) goto failed; + + if (EWOULDBLOCK != syserrno && EAGAIN != syserrno) goto failed; + else + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = addr; + op.arg3.addr_len = &addr_len; + op.timeout = timeout; + op.function = pt_accept_cont; + op.event = POLLIN | POLLPRI; + osfd = pt_Continue(&op); + syserrno = op.syserrno; + } + if (osfd < 0) goto failed; + } + } +#ifdef AIX + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) + addr->inet.family &= 0x00ff; +#endif + newfd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP); + if (newfd == NULL) close(osfd); /* $$$ whoops! this doesn't work $$$ */ + else + { + PR_ASSERT((NULL == addr) || (PR_NETADDR_SIZE(addr) == addr_len)); +#if defined(_PR_INET6) + PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET) + || (addr->raw.family == AF_INET6)); +#else + PR_ASSERT((NULL == addr) || (addr->raw.family == AF_INET)); +#endif + } + return newfd; + +failed: + pt_MapError(_PR_MD_MAP_ACCEPT_ERROR, syserrno); + return NULL; +} /* pt_Accept */ + +static PRStatus pt_Bind(PRFileDesc *fd, const PRNetAddr *addr) +{ + PRIntn rv; + PRInt32 one = 1; + + if (pt_TestAbort()) return PR_FAILURE; + +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + + rv = setsockopt( + fd->secret->md.osfd, SOL_SOCKET, SO_REUSEADDR, + (_PRSockOptVal_t) &one, sizeof(one)); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SETSOCKOPT_ERROR, errno); + return PR_FAILURE; + } + + rv = bind(fd->secret->md.osfd, (struct sockaddr*)addr, PR_NETADDR_SIZE(addr)); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_BIND_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Bind */ + +static PRStatus pt_Listen(PRFileDesc *fd, PRIntn backlog) +{ + PRIntn rv; + + if (pt_TestAbort()) return PR_FAILURE; + + rv = listen(fd->secret->md.osfd, backlog); + if (rv == -1) { + pt_MapError(_PR_MD_MAP_LISTEN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Listen */ + +static PRStatus pt_Shutdown(PRFileDesc *fd, PRIntn how) +{ + PRIntn rv = -1; + if (pt_TestAbort()) return PR_FAILURE; + + rv = shutdown(fd->secret->md.osfd, how); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SHUTDOWN_ERROR, errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* pt_Shutdown */ + +static PRInt32 pt_Recv( + PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + + if (pt_TestAbort()) return PR_FAILURE; + + bytes = recv(fd->secret->md.osfd, buf, amount, flags); + syserrno = errno; + + if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking)) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.timeout = timeout; + op.function = pt_recv_cont; + op.event = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + } + if (bytes < 0) + pt_MapError(_PR_MD_MAP_RECV_ERROR, syserrno); + return bytes; +} /* pt_Recv */ + +static PRInt32 pt_Send( + PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + + /* + * Under HP-UX DCE threads, pthread.h includes dce/cma_ux.h, + * which has the following: + * # define send cma_send + * extern int cma_send (int , void *, int, int ); + * So we need to cast away the 'const' of argument #2 for send(). + */ +#if defined (HPUX) && defined(_PR_DCETHREADS) +#define PT_SENDBUF_CAST (void *) +#else +#define PT_SENDBUF_CAST +#endif + + if (pt_TestAbort()) return PR_FAILURE; + + bytes = send(fd->secret->md.osfd, PT_SENDBUF_CAST buf, amount, flags); + syserrno = errno; + + if ( (bytes >= 0) && (bytes < amount) && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + bytes = -1; + syserrno = ETIMEDOUT; + } + else + { + buf = (char *) buf + bytes; + amount -= bytes; + fNeedContinue = PR_TRUE; + } + } + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + bytes = 0; + fNeedContinue = PR_TRUE; + } + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.timeout = timeout; + op.result.code = bytes; /* initialize the number sent */ + op.function = pt_send_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes == -1) + pt_MapError(_PR_MD_MAP_SEND_ERROR, syserrno); + return bytes; +} /* pt_Send */ + +static PRInt32 pt_SendTo( + PRFileDesc *fd, const void *buf, + PRInt32 amount, PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ + PRInt32 syserrno, bytes = -1; + PRBool fNeedContinue = PR_FALSE; + + if (pt_TestAbort()) return PR_FAILURE; + +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + bytes = sendto( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addr, PR_NETADDR_SIZE(addr)); + syserrno = errno; + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else fNeedContinue = PR_TRUE; + } + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.osfd; + op.arg2.buffer = (void*)buf; + op.arg3.amount = amount; + op.arg4.flags = flags; + op.arg5.addr = (PRNetAddr*)addr; + op.timeout = timeout; + op.result.code = 0; /* initialize the number sent */ + op.function = pt_sendto_cont; + op.event = POLLOUT | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } + if (bytes < 0) + pt_MapError(_PR_MD_MAP_SENDTO_ERROR, syserrno); + return bytes; +} /* pt_SendTo */ + +static PRInt32 pt_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) +{ + PRBool fNeedContinue = PR_FALSE; + PRInt32 syserrno, bytes = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) return PR_FAILURE; + + bytes = recvfrom( + fd->secret->md.osfd, buf, amount, flags, + (struct sockaddr*)addr, &addr_len); + syserrno = errno; + + if ( (bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) + && (!fd->secret->nonblocking) ) + { + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else fNeedContinue = PR_TRUE; + } + + if (fNeedContinue == PR_TRUE) + { + pt_Continuation op; + op.arg1.osfd = fd->secret->md.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 = POLLIN | POLLPRI; + bytes = pt_Continue(&op); + syserrno = op.syserrno; + } +#ifdef AIX + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) addr->inet.family &= 0x00ff; +#endif + if (bytes < 0) + pt_MapError(_PR_MD_MAP_RECVFROM_ERROR, syserrno); + return bytes; +} /* pt_RecvFrom */ + +#ifdef HPUX11 +/* + * pt_HPUXTransmitFile + * + * Send file fd across socket sd. If headers is non-NULL, 'hlen' + * bytes of headers is sent before sending the file. + * + * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file + * + * return number of bytes sent or -1 on error + * + * This implementation takes advantage of the sendfile() system + * call available in HP-UX B.11.00. + */ + +static PRInt32 pt_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, + const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, + PRIntervalTime timeout) +{ + struct stat statbuf; + size_t nbytes_to_send; + struct iovec hdtrl[2]; /* optional header and trailer buffers */ + int send_flags; + PRInt32 count; + int syserrno; + + /* Get file size */ + if (fstat(fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(errno); + return -1; + } + nbytes_to_send = hlen + statbuf.st_size; + + hdtrl[0].iov_base = (void *) headers; /* cast away the 'const' */ + hdtrl[0].iov_len = hlen; + hdtrl[1].iov_base = NULL; + hdtrl[1].iov_base = 0; + /* + * SF_DISCONNECT seems to close the socket even if sendfile() + * only does a partial send on a nonblocking socket. This + * would prevent the subsequent sendfile() calls on that socket + * from working. So we don't use the SD_DISCONNECT flag. + */ + send_flags = 0; + + do { + count = sendfile(sd->secret->md.osfd, fd->secret->md.osfd, + 0, 0, hdtrl, send_flags); + PR_ASSERT(count <= nbytes_to_send); + } while (count == -1 && (syserrno = errno) == EINTR); + + if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) { + count = 0; + } + if (count != -1 && count < nbytes_to_send) { + pt_Continuation op; + + if (count < hlen) { + hdtrl[0].iov_base = ((char *) headers) + count; + hdtrl[0].iov_len = hlen - count; + op.arg3.offset = 0; + } else { + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + op.arg3.offset = count - hlen; + } + + op.arg1.osfd = sd->secret->md.osfd; + op.filedesc = fd->secret->md.osfd; + op.arg2.buffer = hdtrl; + op.arg4.flags = send_flags; + op.nbytes_to_send = nbytes_to_send - count; + op.result.code = count; + op.timeout = timeout; + op.function = pt_hpux_transmitfile_cont; + op.event = POLLOUT | POLLPRI; + count = pt_Continue(&op); + syserrno = op.syserrno; + } + + if (count == -1) { + _MD_hpux_map_sendfile_error(syserrno); + return -1; + } + if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { + PR_Close(sd); + } + return count; +} +#endif /* HPUX11 */ + +static PRInt32 pt_TransmitFile( + PRFileDesc *sd, PRFileDesc *fd, const void *headers, + PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + if (pt_TestAbort()) return -1; + +#ifdef HPUX11 + return pt_HPUXTransmitFile(sd, fd, headers, hlen, flags, timeout); +#else + return _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout); +#endif +} /* pt_TransmitFile */ + +/* + * XXX: When IPv6 is running, we need to see if this code works + * with a PRNetAddr structure that supports both IPv4 and IPv6. + */ +static PRInt32 pt_AcceptRead( + PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, + void *buf, PRInt32 amount, PRIntervalTime timeout) +{ + PRInt32 rv = -1; + PRNetAddr remote; + PRFileDesc *accepted = NULL; + PRIntervalTime start, elapsed; + + if (pt_TestAbort()) return rv; + + if (PR_INTERVAL_NO_TIMEOUT != timeout) start = PR_IntervalNow(); + if ((accepted = PR_Accept(sd, &remote, timeout)) == NULL) return rv; + + if (PR_INTERVAL_NO_TIMEOUT != timeout) + { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) + { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + goto failed; + } + else timeout = timeout - elapsed; + } + + rv = PR_Recv(accepted, buf, amount, 0, timeout); + if (rv >= 0) + { + /* copy the new info out where caller can see it */ + *nd = accepted; + *raddr = (PRNetAddr *)((char*)buf + amount); + memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote)); + return rv; + } + +failed: + PR_Close(accepted); + return rv; +} /* pt_AcceptRead */ + +static PRStatus pt_GetSockName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) return PR_FAILURE; + + rv = getsockname( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); +#ifdef AIX + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) addr->inet.family &= 0x00ff; +#endif + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETSOCKNAME_ERROR, errno); + return PR_FAILURE; + } else { + PR_ASSERT(addr_len == PR_NETADDR_SIZE(addr)); +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + return PR_SUCCESS; + } +} /* pt_GetSockName */ + +static PRStatus pt_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) +{ + PRIntn rv = -1; + pt_SockLen addr_len = sizeof(PRNetAddr); + + if (pt_TestAbort()) return PR_FAILURE; + + rv = getpeername( + fd->secret->md.osfd, (struct sockaddr*)addr, &addr_len); + +#ifdef AIX + /* mask off the first byte of struct sockaddr (the length field) */ + if (addr) addr->inet.family &= 0x00ff; +#endif + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETPEERNAME_ERROR, errno); + return PR_FAILURE; + } else { + PR_ASSERT(addr_len == PR_NETADDR_SIZE(addr)); +#if defined(_PR_INET6) + PR_ASSERT(addr->raw.family == AF_INET || addr->raw.family == AF_INET6); +#else + PR_ASSERT(addr->raw.family == AF_INET); +#endif + return PR_SUCCESS; + } +} /* pt_GetPeerName */ + +static PRStatus pt_GetSockOpt( + PRFileDesc *fd, PRSockOption optname, void* optval, PRInt32* optlen) +{ + PRIntn rv = -1; + PRInt32 level, name; + + if (pt_TestAbort()) return PR_FAILURE; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call. + */ + if (PR_SockOpt_Nonblocking == optname) + { + PR_ASSERT(sizeof(PRIntn) <= *optlen); + *((PRIntn *) optval) = (PRIntn) fd->secret->nonblocking; + *optlen = sizeof(PRIntn); + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(optname, &level, &name); + if (0 == rv) + { + if (PR_SockOpt_Linger == optname) + { + struct linger linger; + pt_SockLen len = sizeof(linger); + rv = getsockopt(fd->secret->md.osfd, level, name, + (char *) &linger, &len); + if (0 == rv) + { + ((PRLinger*)(optval))->polarity = linger.l_onoff + ? PR_TRUE : PR_FALSE; + ((PRLinger*)(optval))->linger = PR_SecondsToInterval( + linger.l_linger); + *optlen = sizeof(PRLinger); + } + } + else + { + /* Make sure the pointer type cast below is safe */ + PR_ASSERT(sizeof(PRInt32) == sizeof(PRIntn)); + rv = getsockopt(fd->secret->md.osfd, level, name, + optval, (pt_SockLen*)optlen); + } + } + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_GETSOCKOPT_ERROR, errno); + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} /* pt_GetSockOpt */ + +static PRStatus pt_SetSockOpt( + PRFileDesc *fd, PRSockOption optname, const void* optval, PRInt32 optlen) +{ + PRIntn rv = -1; + PRInt32 level, name; + + if (pt_TestAbort()) return PR_FAILURE; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt() call. + */ + if (PR_SockOpt_Nonblocking == optname) + { + PR_ASSERT(sizeof(PRIntn) == optlen); + fd->secret->nonblocking = *((PRIntn *) optval) ? PR_TRUE : PR_FALSE; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(optname, &level, &name); + if (0 == rv) + { + if (PR_SockOpt_Linger == optname) + { + struct linger linger; + linger.l_onoff = ((PRLinger*)(optval))->polarity; + linger.l_linger = PR_IntervalToSeconds( + ((PRLinger*)(optval))->linger); + rv = setsockopt(fd->secret->md.osfd, level, name, + (char *) &linger, sizeof(linger)); + } + else + { + rv = setsockopt(fd->secret->md.osfd, level, name, + optval, optlen); + } + } + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_SETSOCKOPT_ERROR, errno); + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} /* pt_SetSockOpt */ + +static PRStatus pt_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data) +{ + PRIntn rv; + pt_SockLen length; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a getsockopt() call + */ + if (PR_SockOpt_Nonblocking == data->option) + { + data->value.non_blocking = fd->secret->nonblocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + length = sizeof(linger); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char *) &linger, &length); + PR_ASSERT((-1 == rv) || (sizeof(linger) == length)); + data->value.linger.polarity = + (linger.l_onoff) ? PR_TRUE : PR_FALSE; + data->value.linger.linger = + PR_SecondsToInterval(linger.l_linger); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.reuse_addr = (0 == value) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 boolean; + length = sizeof(boolean); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&boolean, &length); + PR_ASSERT((-1 == rv) || (sizeof(boolean) == length)); + data->value.mcast_loopback = (0 == boolean) ? PR_FALSE : PR_TRUE; + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value; + length = sizeof(PRIntn); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&value, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + data->value.recv_buffer_size = value; + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + length = sizeof(PRUintn); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(PRIntn) == length)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl; + length = sizeof(ttl); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, &length); + PR_ASSERT((-1 == rv) || (sizeof(ttl) == length)); + data->value.mcast_ttl = ttl; + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + length = sizeof(mreq); + rv = getsockopt( + fd->secret->md.osfd, level, name, (char*)&mreq, &length); + PR_ASSERT((-1 == rv) || (sizeof(mreq) == length)); + data->value.add_member.mcaddr.inet.ip = + mreq.imr_multiaddr.s_addr; + data->value.add_member.ifaddr.inet.ip = + mreq.imr_interface.s_addr; + break; + } + case PR_SockOpt_McastInterface: + { + length = sizeof(data->value.mcast_if.inet.ip); + rv = getsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, &length); + PR_ASSERT((-1 == rv) + || (sizeof(data->value.mcast_if.inet.ip) == length)); + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) _PR_MD_MAP_GETSOCKOPT_ERROR(errno); + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_GetSocketOption */ + +static PRStatus pt_SetSocketOption(PRFileDesc *fd, const PRSocketOptionData *data) +{ + PRIntn rv; + PRInt32 level, name; + + /* + * PR_SockOpt_Nonblocking is a special case that does not + * translate to a setsockopt call. + */ + if (PR_SockOpt_Nonblocking == data->option) + { + fd->secret->nonblocking = data->value.non_blocking; + return PR_SUCCESS; + } + + rv = _PR_MapOptionName(data->option, &level, &name); + if (PR_SUCCESS == rv) + { + switch (data->option) + { + case PR_SockOpt_Linger: + { + struct linger linger; + linger.l_onoff = data->value.linger.polarity; + linger.l_linger = PR_IntervalToSeconds(data->value.linger.linger); + rv = setsockopt( + fd->secret->md.osfd, level, name, (char*)&linger, sizeof(linger)); + break; + } + case PR_SockOpt_Reuseaddr: + case PR_SockOpt_Keepalive: + case PR_SockOpt_NoDelay: + { + PRIntn value = (data->value.reuse_addr) ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); + break; + } + case PR_SockOpt_McastLoopback: + { + PRUint8 boolean = data->value.mcast_loopback ? 1 : 0; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&boolean, sizeof(boolean)); + break; + } + case PR_SockOpt_RecvBufferSize: + case PR_SockOpt_SendBufferSize: + case PR_SockOpt_MaxSegment: + { + PRIntn value = data->value.recv_buffer_size; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&value, sizeof(PRIntn)); + break; + } + case PR_SockOpt_IpTimeToLive: + case PR_SockOpt_IpTypeOfService: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.ip_ttl, sizeof(PRUintn)); + break; + } + case PR_SockOpt_McastTimeToLive: + { + PRUint8 ttl = data->value.mcast_ttl; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&ttl, sizeof(ttl)); + break; + } + case PR_SockOpt_AddMember: + case PR_SockOpt_DropMember: + { + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = + data->value.add_member.mcaddr.inet.ip; + mreq.imr_interface.s_addr = + data->value.add_member.ifaddr.inet.ip; + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&mreq, sizeof(mreq)); + break; + } + case PR_SockOpt_McastInterface: + { + rv = setsockopt( + fd->secret->md.osfd, level, name, + (char*)&data->value.mcast_if.inet.ip, + sizeof(data->value.mcast_if.inet.ip)); + break; + } + default: + PR_NOT_REACHED("Unknown socket option"); + break; + } + if (-1 == rv) _PR_MD_MAP_SETSOCKOPT_ERROR(errno); + } + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* pt_SetSocketOption */ + +/*****************************************************************************/ +/****************************** I/O method objects ***************************/ +/*****************************************************************************/ + +static PRIOMethods _pr_file_methods = { + PR_DESC_FILE, + pt_Close, + pt_Read, + pt_Write, + pt_Available, + pt_Available64, + pt_Fsync, + pt_Seek, + pt_Seek64, + pt_FileInfo, + pt_FileInfo64, + (PRWritevFN)_PR_InvalidInt, + (PRConnectFN)_PR_InvalidStatus, + (PRAcceptFN)_PR_InvalidDesc, + (PRBindFN)_PR_InvalidStatus, + (PRListenFN)_PR_InvalidStatus, + (PRShutdownFN)_PR_InvalidStatus, + (PRRecvFN)_PR_InvalidInt, + (PRSendFN)_PR_InvalidInt, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + (PRPollFN)0, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRGetsockoptFN)_PR_InvalidStatus, + (PRSetsockoptFN)_PR_InvalidStatus, +}; + +static PRIOMethods _pr_tcp_methods = { + PR_DESC_SOCKET_TCP, + pt_Close, + pt_Read, + pt_Write, + pt_Available, + pt_Available64, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + pt_Accept, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + (PRRecvfromFN)_PR_InvalidInt, + (PRSendtoFN)_PR_InvalidInt, + (PRPollFN)0, + pt_AcceptRead, + pt_TransmitFile, + pt_GetSockName, + pt_GetPeerName, + pt_GetSockOpt, + pt_SetSockOpt, + pt_GetSocketOption, + pt_SetSocketOption +}; + +static PRIOMethods _pr_udp_methods = { + PR_DESC_SOCKET_UDP, + pt_Close, + pt_Read, + pt_Write, + pt_Available, + pt_Available64, + pt_Synch, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + pt_Writev, + pt_Connect, + (PRAcceptFN)_PR_InvalidDesc, + pt_Bind, + pt_Listen, + pt_Shutdown, + pt_Recv, + pt_Send, + pt_RecvFrom, + pt_SendTo, + (PRPollFN)0, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + pt_GetSockName, + pt_GetPeerName, + pt_GetSockOpt, + pt_SetSockOpt, + pt_GetSocketOption, + pt_SetSocketOption +}; + +#if defined(_PR_FCNTL_FLAGS) +#undef _PR_FCNTL_FLAGS +#endif + +#if defined(HPUX) || defined(OSF1) || defined(SOLARIS) || defined (IRIX) \ + || defined(AIX) || defined(LINUX) || defined(FREEBSD) +#define _PR_FCNTL_FLAGS O_NONBLOCK +#else +#error "Can't determine architecture" +#endif + +static PRFileDesc *pt_SetMethods(PRIntn osfd, PRDescType type) +{ + PRInt32 flags, one = 1; + PRFileDesc *fd = pt_Getfd(); + + if (fd == NULL) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + else + { + fd->secret->md.osfd = osfd; + fd->secret->state = _PR_FILEDESC_OPEN; + switch (type) + { + case PR_DESC_FILE: + fd->methods = PR_GetFileMethods(); + break; + case PR_DESC_SOCKET_TCP: + fd->methods = PR_GetTCPMethods(); + flags = fcntl(osfd, F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(osfd, F_SETFL, flags); + (void)setsockopt(osfd, SOL_SOCKET, SO_KEEPALIVE, + (_PRSockOptVal_t) &one, sizeof(one)); + break; + case PR_DESC_SOCKET_UDP: + fd->methods = PR_GetUDPMethods(); + flags = fcntl(osfd, F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(osfd, F_SETFL, flags); + break; + default: + break; + } + } + return fd; +} /* pt_SetMethods */ + +PR_IMPLEMENT(PRIOMethods*) PR_GetFileMethods() +{ + return &_pr_file_methods; +} /* PR_GetFileMethods */ + +PR_IMPLEMENT(PRIOMethods*) PR_GetTCPMethods() +{ + return &_pr_tcp_methods; +} /* PR_GetTCPMethods */ + +PR_IMPLEMENT(PRIOMethods*) PR_GetUDPMethods() +{ + return &_pr_udp_methods; +} /* PR_GetUDPMethods */ + +PR_IMPLEMENT(PRFileDesc*) PR_AllocFileDesc(PRInt32 osfd, PRIOMethods *methods) +{ + PRFileDesc *fd = pt_Getfd(); + + /* + * Assert that the file descriptor is small enough to fit in the + * fd_set passed to select + */ + PR_ASSERT(osfd < FD_SETSIZE); + if (NULL == fd) goto failed; + + fd->methods = methods; + fd->secret->md.osfd = osfd; + /* Make fd non-blocking */ + if (osfd > 2) + { + /* Don't mess around with stdin, stdout or stderr */ + PRIntn flags; + flags = fcntl(osfd, F_GETFL, 0); + fcntl(osfd, F_SETFL, flags | _PR_FCNTL_FLAGS); + } + fd->secret->state = _PR_FILEDESC_OPEN; + return fd; + +failed: + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return fd; +} /* PR_AllocFileDesc */ + +PR_IMPLEMENT(PRFileDesc*) PR_Socket(PRInt32 domain, PRInt32 type, PRInt32 proto) +{ + PRIntn osfd; + PRDescType ftype; + PRFileDesc *fd = NULL; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (pt_TestAbort()) return NULL; + + if (PF_INET != domain +#if defined(_PR_INET6) + && PF_INET6 != domain +#endif + ) + { + PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } + if (type == SOCK_STREAM) ftype = PR_DESC_SOCKET_TCP; + else if (type == SOCK_DGRAM) ftype = PR_DESC_SOCKET_UDP; + else + { + (void)PR_SetError(PR_ADDRESS_NOT_SUPPORTED_ERROR, 0); + return fd; + } + + osfd = socket(domain, type, proto); + if (osfd == -1) pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno); + else + { + fd = pt_SetMethods(osfd, ftype); + if (fd == NULL) close(osfd); + } + return fd; +} /* PR_Socket */ + +/*****************************************************************************/ +/****************************** I/O public methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_Open(const char *name, PRIntn flags, PRIntn mode) +{ + PRFileDesc *fd = NULL; + PRIntn syserrno, osfd = -1, osflags = 0;; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (pt_TestAbort()) return NULL; + + if (flags & PR_RDONLY) osflags |= O_RDONLY; + if (flags & PR_WRONLY) osflags |= O_WRONLY; + if (flags & PR_RDWR) osflags |= O_RDWR; + if (flags & PR_APPEND) osflags |= O_APPEND; + if (flags & PR_TRUNCATE) osflags |= O_TRUNC; + + /* + ** We have to hold the lock across the creation in order to + ** enforce the sematics of PR_Rename(). (see the latter for + ** more details) + */ + if (flags & PR_CREATE_FILE) + { + osflags |= O_CREAT; + if (NULL !=_pr_rename_lock) + PR_Lock(_pr_rename_lock); + } + + osfd = open(name, osflags, mode); + syserrno = errno; + + if ((flags & PR_CREATE_FILE) && (NULL !=_pr_rename_lock)) + PR_Unlock(_pr_rename_lock); + + if (osfd == -1) + pt_MapError(_PR_MD_MAP_OPEN_ERROR, syserrno); + else + { + fd = pt_SetMethods(osfd, PR_DESC_FILE); + if (fd == NULL) close(osfd); /* $$$ whoops! this is bad $$$ */ + } + return fd; +} /* PR_Open */ + +PR_IMPLEMENT(PRStatus) PR_Delete(const char *name) +{ + PRIntn rv = -1; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (pt_TestAbort()) return PR_FAILURE; + + rv = unlink(name); + + if (rv == -1) { + pt_MapError(_PR_MD_MAP_UNLINK_ERROR, errno); + return PR_FAILURE; + } else + return PR_SUCCESS; +} /* PR_Delete */ + +PR_IMPLEMENT(PRStatus) PR_Access(const char *name, PRAccessHow how) +{ + PRIntn rv; + + if (pt_TestAbort()) return PR_FAILURE; + + switch (how) + { + case PR_ACCESS_READ_OK: + rv = access(name, R_OK); + break; + case PR_ACCESS_WRITE_OK: + rv = access(name, W_OK); + break; + case PR_ACCESS_EXISTS: + default: + rv = access(name, F_OK); + } + if (0 == rv) return PR_SUCCESS; + pt_MapError(_PR_MD_MAP_ACCESS_ERROR, errno); + return PR_FAILURE; + +} /* PR_Access */ + +PR_IMPLEMENT(PRStatus) PR_GetFileInfo(const char *fn, PRFileInfo *info) +{ + PRInt32 rv; + struct stat sb; + PRInt64 s, s2us; + + if (pt_TestAbort()) return PR_FAILURE; + + 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; +#if defined(IRIX) && defined(HAVE_LONG_LONG) + info->modifyTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_mtim.tv_sec); + info->creationTime = (PR_USEC_PER_SEC * (PRInt64)sb.st_ctim.tv_sec); +#else + LL_I2L(s, sb.st_mtime); + LL_I2L(s2us, PR_USEC_PER_SEC); + LL_MUL(s, s, s2us); + info->modifyTime = s; + LL_I2L(s, sb.st_ctime); + LL_MUL(s, s, s2us); + info->creationTime = s; +#endif + } + } + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_GetFileInfo */ + +PR_IMPLEMENT(PRStatus) PR_Rename(const char *from, const char *to) +{ + PRIntn rv = -1; + + if (pt_TestAbort()) return PR_FAILURE; + + /* + ** We have to acquire a lock here to stiffle anybody trying to create + ** a new file at the same time. And we have to hold that lock while we + ** test to see if the file exists and do the rename. The other place + ** where the lock is held is in PR_Open() when possibly creating a + ** new file. + */ + + PR_Lock(_pr_rename_lock); + rv = access(to, F_OK); + if (0 == rv) + { + PR_SetError(PR_FILE_EXISTS_ERROR, 0); + rv = -1; + } + else + { + rv = rename(from, to); + if (rv == -1) + pt_MapError(_PR_MD_MAP_RENAME_ERROR, errno); + } + PR_Unlock(_pr_rename_lock); + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Rename */ + +PR_IMPLEMENT(PRStatus) PR_CloseDir(PRDir *dir) +{ + if (pt_TestAbort()) return PR_FAILURE; + + if (NULL != dir->md.d) + { + closedir(dir->md.d); + dir->md.d = NULL; + } + return PR_SUCCESS; +} /* PR_CloseDir */ + +PR_IMPLEMENT(PRStatus) PR_MkDir(const char *name, PRIntn mode) +{ + PRInt32 rv = -1; + + if (pt_TestAbort()) return PR_FAILURE; + + /* + ** This lock is used to enforce rename semantics as described + ** in PR_Rename. + */ + if (NULL !=_pr_rename_lock) + PR_Lock(_pr_rename_lock); + rv = mkdir(name, mode); + if (-1 == rv) + pt_MapError(_PR_MD_MAP_MKDIR_ERROR, errno); + if (NULL !=_pr_rename_lock) + PR_Unlock(_pr_rename_lock); + + return (-1 == rv) ? PR_FAILURE : PR_SUCCESS; +} /* PR_Mkdir */ + +PR_IMPLEMENT(PRStatus) PR_RmDir(const char *name) +{ + PRInt32 rv; + + if (pt_TestAbort()) return PR_FAILURE; + + rv = rmdir(name); + if (0 == rv) { + return PR_SUCCESS; + } else { + pt_MapError(_PR_MD_MAP_RMDIR_ERROR, errno); + return PR_FAILURE; + } +} /* PR_Rmdir */ + + +PR_IMPLEMENT(PRDir*) PR_OpenDir(const char *name) +{ + DIR *osdir; + PRDir *dir = NULL; + + if (pt_TestAbort()) return dir; + + osdir = opendir(name); + if (osdir == NULL) + pt_MapError(_PR_MD_MAP_OPENDIR_ERROR, errno); + else + { + dir = PR_NEWZAP(PRDir); + dir->md.d = osdir; + } + return dir; +} /* PR_OpenDir */ + +PR_IMPLEMENT(PRInt32) PR_Poll( + PRPollDesc *pds, PRIntn npds, PRIntervalTime timeout) +{ + PRInt32 ready = 0; + /* + * For restarting poll() if it is interrupted by a signal. + * We use these variables to figure out how much time has + * elapsed and how much of the timeout still remains. + */ + PRIntervalTime start, elapsed, remaining; + + if (0 == npds) PR_Sleep(timeout); + else + { + PRIntn index, msecs; + struct pollfd *syspoll = NULL; + syspoll = (struct pollfd*)PR_Malloc(npds * sizeof(struct pollfd)); + for (index = 0; index < npds; ++index) + { + PRFileDesc *bottom = pds[index].fd; + + if (bottom == NULL) + { + /* make poll() ignore this entry */ + syspoll[index].fd = -1; + continue; + } + + while (bottom->lower != NULL) bottom = bottom->lower; + syspoll[index].fd = bottom->secret->md.osfd; + + syspoll[index].events = 0; + if (pds[index].in_flags & PR_POLL_READ) + syspoll[index].events |= POLLIN; + if (pds[index].in_flags & PR_POLL_WRITE) + syspoll[index].events |= POLLOUT; + if (pds[index].in_flags & PR_POLL_EXCEPT) + syspoll[index].events |= POLLPRI; + if (_PR_FILEDESC_OPEN == bottom->secret->state) + pds[index].out_flags = 0; /* init the result */ + else + { + ready += 1; /* this will cause an abrupt return */ + pds[index].out_flags = POLLNVAL; /* bogii */ + } + } + if (0 == ready) + { + switch (timeout) + { + case PR_INTERVAL_NO_WAIT: msecs = 0; break; + case PR_INTERVAL_NO_TIMEOUT: msecs = -1; break; + default: + msecs = PR_IntervalToMilliseconds(timeout); + start = PR_IntervalNow(); + } + +retry: + ready = poll(syspoll, npds, msecs); + if (-1 == ready) + { + PRIntn oserror = errno; + PRErrorCode prerror; + + switch (oserror) { + case EAGAIN: + prerror = PR_INSUFFICIENT_RESOURCES_ERROR; + break; + case EINTR: + if (timeout == PR_INTERVAL_NO_TIMEOUT) + goto retry; + else if (timeout == PR_INTERVAL_NO_WAIT) + ready = 0; /* don't retry, just time out */ + { + elapsed = (PRIntervalTime) (PR_IntervalNow() + - start); + if (elapsed > timeout) + ready = 0; /* timed out */ + else + { + remaining = timeout - elapsed; + msecs = PR_IntervalToMilliseconds(remaining); + goto retry; + } + } + break; + case EINVAL: + prerror = PR_INVALID_ARGUMENT_ERROR; + break; + case EFAULT: + prerror = PR_ACCESS_FAULT_ERROR; + break; + default: + prerror = PR_UNKNOWN_ERROR; + break; + } + PR_SetError(prerror, oserror); + } + else if (ready > 0) + { + for (index = 0; index < npds; ++index) + { + if (pds[index].fd == NULL) continue; + PR_ASSERT(0 == pds[index].out_flags); + if (0 != syspoll[index].revents) + { + if (syspoll[index].revents & POLLIN) + pds[index].out_flags |= PR_POLL_READ; + if (syspoll[index].revents & POLLOUT) + pds[index].out_flags |= PR_POLL_WRITE; + if (syspoll[index].revents & POLLPRI) + pds[index].out_flags |= PR_POLL_EXCEPT; + if (syspoll[index].revents & POLLERR) + pds[index].out_flags |= PR_POLL_ERR; + if (syspoll[index].revents & POLLNVAL) + pds[index].out_flags |= PR_POLL_NVAL; + } + } + } + } + + PR_DELETE(syspoll); + + } + return ready; + +} /* PR_Poll */ + +PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) +{ + struct dirent *dp; + + if (pt_TestAbort()) return NULL; + + for (;;) + { + dp = readdir(dir->md.d); + if (NULL == dp) return NULL; + if ((flags & PR_SKIP_DOT) + && ('.' == dp->d_name[0]) + && (0 == dp->d_name[1])) continue; + if ((flags & PR_SKIP_DOT_DOT) + && ('.' == dp->d_name[0]) + && ('.' == dp->d_name[1]) + && (0 == dp->d_name[2])) continue; + if ((flags & PR_SKIP_HIDDEN) && ('.' == dp->d_name[0])) + continue; + break; + } + dir->d.name = dp->d_name; + return &dir->d; +} /* PR_ReadDir */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket() +{ + PRFileDesc *fd = NULL; + PRIntn osfd = -1, syserrno; + PRIntn domain = PF_INET; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (pt_TestAbort()) return NULL; + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) + domain = PF_INET6; +#endif + osfd = socket(domain, SOCK_DGRAM, 0); + syserrno = errno; + + if (osfd == -1) + pt_MapError(_PR_MD_MAP_SOCKET_ERROR, syserrno); + else + { + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP); + if (fd == NULL) close(osfd); + } + return fd; +} /* PR_NewUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_NewTCPSocket() +{ + PRIntn osfd = -1; + PRFileDesc *fd = NULL; + PRIntn domain = PF_INET; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (pt_TestAbort()) return NULL; + +#if defined(_PR_INET6) + if (_pr_ipv6_enabled) + domain = PF_INET6; +#endif + osfd = socket(domain, SOCK_STREAM, 0); + + if (osfd == -1) + pt_MapError(_PR_MD_MAP_SOCKET_ERROR, errno); + else + { + fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP); + if (fd == NULL) close(osfd); + } + return fd; +} /* PR_NewTCPSocket */ + +PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]) +{ + PRInt32 osfd[2]; + + if (pt_TestAbort()) return PR_FAILURE; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, osfd) == -1) { + pt_MapError(_PR_MD_MAP_SOCKETPAIR_ERROR, errno); + return PR_FAILURE; + } + + fds[0] = pt_SetMethods(osfd[0], PR_DESC_SOCKET_TCP); + if (fds[0] == NULL) { + close(osfd[0]); + close(osfd[1]); + return PR_FAILURE; + } + fds[1] = pt_SetMethods(osfd[1], PR_DESC_SOCKET_TCP); + if (fds[1] == NULL) { + PR_Close(fds[0]); + close(osfd[1]); + return PR_FAILURE; + } + return PR_SUCCESS; +} /* PR_NewTCPSocketPair */ + +PR_IMPLEMENT(PRStatus) PR_CreatePipe( + PRFileDesc **readPipe, + PRFileDesc **writePipe +) +{ + int pipefd[2]; + int flags; + + if (pt_TestAbort()) return PR_FAILURE; + + if (pipe(pipefd) == -1) + { + /* XXX map pipe error */ + PR_SetError(PR_UNKNOWN_ERROR, errno); + return PR_FAILURE; + } + *readPipe = pt_SetMethods(pipefd[0], PR_DESC_FILE); + if (NULL == *readPipe) + { + close(pipefd[0]); + close(pipefd[1]); + return PR_FAILURE; + } + flags = fcntl(pipefd[0], F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(pipefd[0], F_SETFL, flags); + *writePipe = pt_SetMethods(pipefd[1], PR_DESC_FILE); + if (NULL == *writePipe) + { + PR_Close(*readPipe); + close(pipefd[1]); + return PR_FAILURE; + } + flags = fcntl(pipefd[1], F_GETFL, 0); + flags |= _PR_FCNTL_FLAGS; + (void)fcntl(pipefd[1], F_SETFL, flags); + return PR_SUCCESS; +} + +/*****************************************************************************/ +/***************************** I/O friends methods ***************************/ +/*****************************************************************************/ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportFile(PRInt32 osfd) +{ + PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_FILE); + if (NULL == fd) close(osfd); + return fd; +} /* PR_ImportFile */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportTCPSocket(PRInt32 osfd) +{ + PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_SOCKET_TCP); + if (NULL == fd) close(osfd); + return fd; +} /* PR_ImportTCPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_ImportUDPSocket(PRInt32 osfd) +{ + PRFileDesc *fd = pt_SetMethods(osfd, PR_DESC_SOCKET_UDP); + if (NULL != fd) close(osfd); + return fd; +} /* PR_ImportUDPSocket */ + +PR_IMPLEMENT(PRInt32) PR_FileDesc2NativeHandle(PRFileDesc *fd) +{ + if (fd) + { + /* + * The fd may be layered. Chase the links to the + * bottom layer to get the osfd. + */ + PRFileDesc *bottom = fd; + while (bottom->lower != NULL) { + bottom = bottom->lower; + } + return bottom->secret->md.osfd; + } + else + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +} /* PR_FileDesc2NativeHandle */ + +PR_IMPLEMENT(void) PR_ChangeFileDescNativeHandle(PRFileDesc *fd, + PRInt32 handle) +{ + if (fd) fd->secret->md.osfd = handle; +} /* PR_ChangeFileDescNativeHandle*/ + +PR_IMPLEMENT(PRStatus) PR_LockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) return PR_FAILURE; + + PR_Lock(_pr_flock_lock); + if (0 == fd->secret->lockCount) + { + status = _PR_MD_LOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) fd->secret->lockCount = 1; + } + else fd->secret->lockCount += 1; + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_LockFile */ + +PR_IMPLEMENT(PRStatus) PR_TLockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) return PR_FAILURE; + + PR_Lock(_pr_flock_lock); + if (0 == fd->secret->lockCount) + { + status = _PR_MD_TLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) fd->secret->lockCount = 1; + } + else fd->secret->lockCount += 1; + PR_Unlock(_pr_flock_lock); + + return status; +} /* PR_TLockFile */ + +PR_IMPLEMENT(PRStatus) PR_UnlockFile(PRFileDesc *fd) +{ + PRStatus status = PR_SUCCESS; + + if (pt_TestAbort()) return PR_FAILURE; + + PR_Lock(_pr_flock_lock); + if (fd->secret->lockCount == 1) + { + status = _PR_MD_UNLOCKFILE(fd->secret->md.osfd); + if (PR_SUCCESS == status) fd->secret->lockCount = 0; + } + else fd->secret->lockCount -= 1; + PR_Unlock(_pr_flock_lock); + + return status; +} + +/* + * The next two entry points should not be in the API, but they are + * defined here for historical (or hysterical) reasons. + */ + +PRInt32 PR_GetSysfdTableMax(void) +{ +#if defined(XP_UNIX) && !defined(AIX) + struct rlimit rlim; + + if ( getrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + + return rlim.rlim_max; +#elif defined(AIX) + return sysconf(_SC_OPEN_MAX); +#endif +} + +PRInt32 PR_SetSysfdTableSize(PRIntn table_size) +{ +#if defined(XP_UNIX) && !defined(AIX) + struct rlimit rlim; + PRInt32 tableMax = PR_GetSysfdTableMax(); + + if (tableMax < 0) return -1; + rlim.rlim_max = tableMax; + + /* Grow as much as we can; even if too big */ + if ( rlim.rlim_max < table_size ) + rlim.rlim_cur = rlim.rlim_max; + else + rlim.rlim_cur = table_size; + + if ( setrlimit(RLIMIT_NOFILE, &rlim) < 0) + return -1; + + return rlim.rlim_cur; +#elif defined(AIX) + return -1; +#endif +} + +/* + * PR_Stat is supported for backward compatibility; some existing Java + * code uses it. New code should use PR_GetFileInfo. + */ + +#ifndef NO_NSPR_10_SUPPORT +PR_IMPLEMENT(PRInt32) PR_Stat(const char *name, struct stat *buf) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_Stat", "PR_GetFileInfo"); + + if (pt_TestAbort()) return PR_FAILURE; + + if (-1 == stat(name, buf)) { + pt_MapError(_PR_MD_MAP_STAT_ERROR, errno); + return PR_FAILURE; + } else { + return PR_SUCCESS; + } +} +#endif /* ! NO_NSPR_10_SUPPORT */ + + +PR_IMPLEMENT(void) PR_FD_ZERO(PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_ZERO (PR_Select)", "PR_Poll"); + memset(set, 0, sizeof(PR_fd_set)); +} + +PR_IMPLEMENT(void) PR_FD_SET(PRFileDesc *fh, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_SET (PR_Select)", "PR_Poll"); + PR_ASSERT( set->hsize < PR_MAX_SELECT_DESC ); + + set->harray[set->hsize++] = fh; +} + +PR_IMPLEMENT(void) PR_FD_CLR(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_CLR (PR_Select)", "PR_Poll"); + + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + for (index2=index; index2 < (set->hsize-1); index2++) { + set->harray[index2] = set->harray[index2+1]; + } + set->hsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_ISSET(PRFileDesc *fh, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_ISSET (PR_Select)", "PR_Poll"); + for (index = 0; index<set->hsize; index++) + if (set->harray[index] == fh) { + return 1; + } + return 0; +} + +PR_IMPLEMENT(void) PR_FD_NSET(PRInt32 fd, PR_fd_set *set) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_NSET (PR_Select)", "PR_Poll"); + PR_ASSERT( set->nsize < PR_MAX_SELECT_DESC ); + + set->narray[set->nsize++] = fd; +} + +PR_IMPLEMENT(void) PR_FD_NCLR(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index, index2; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_NCLR (PR_Select)", "PR_Poll"); + + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + for (index2=index; index2 < (set->nsize-1); index2++) { + set->narray[index2] = set->narray[index2+1]; + } + set->nsize--; + break; + } +} + +PR_IMPLEMENT(PRInt32) PR_FD_NISSET(PRInt32 fd, PR_fd_set *set) +{ + PRUint32 index; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete("PR_FD_NISSET (PR_Select)", "PR_Poll"); + for (index = 0; index<set->nsize; index++) + if (set->narray[index] == fd) { + return 1; + } + return 0; +} + +#include <sys/types.h> +#include <sys/time.h> +#if !defined(SUNOS4) && !defined(HPUX) && !defined(LINUX) +#include <sys/select.h> +#endif + +static PRInt32 +_PR_getset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index; + PRInt32 max = 0; + + if (!pr_set) + return 0; + + FD_ZERO(set); + + /* First set the pr file handle osfds */ + for (index=0; index<pr_set->hsize; index++) { + FD_SET(pr_set->harray[index]->secret->md.osfd, set); + if (pr_set->harray[index]->secret->md.osfd > max) + max = pr_set->harray[index]->secret->md.osfd; + } + /* Second set the native osfds */ + for (index=0; index<pr_set->nsize; index++) { + FD_SET(pr_set->narray[index], set); + if (pr_set->narray[index] > max) + max = pr_set->narray[index]; + } + return max; +} + +static void +_PR_setset(PR_fd_set *pr_set, fd_set *set) +{ + PRUint32 index, last_used; + + if (!pr_set) + return; + + for (last_used=0, index=0; index<pr_set->hsize; index++) { + if ( FD_ISSET(pr_set->harray[index]->secret->md.osfd, set) ) { + pr_set->harray[last_used++] = pr_set->harray[index]; + } + } + pr_set->hsize = last_used; + + for (last_used=0, index=0; index<pr_set->nsize; index++) { + if ( FD_ISSET(pr_set->narray[index], set) ) { + pr_set->narray[last_used++] = pr_set->narray[index]; + } + } + pr_set->nsize = last_used; +} + +PR_IMPLEMENT(PRInt32) PR_Select( + PRInt32 unused, PR_fd_set *pr_rd, PR_fd_set *pr_wr, + PR_fd_set *pr_ex, PRIntervalTime timeout) +{ + fd_set rd, wr, ex; + struct timeval tv, *tvp; + PRInt32 max, max_fd; + PRInt32 rv; + /* + * For restarting select() if it is interrupted by a Unix signal. + * We use these variables to figure out how much time has elapsed + * and how much of the timeout still remains. + */ + PRIntervalTime start, elapsed, remaining; + + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( "PR_Select", "PR_Poll"); + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + + max_fd = _PR_getset(pr_rd, &rd); + max_fd = (max = _PR_getset(pr_wr, &wr))>max_fd?max:max_fd; + max_fd = (max = _PR_getset(pr_ex, &ex))>max_fd?max:max_fd; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + tvp = NULL; + } else { + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(timeout); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + timeout - PR_SecondsToInterval(tv.tv_sec)); + tvp = &tv; + start = PR_IntervalNow(); + } + +retry: + rv = select(max_fd + 1, (_PRSelectFdSetArg_t) &rd, + (_PRSelectFdSetArg_t) &wr, (_PRSelectFdSetArg_t) &ex, tvp); + + if (rv == -1 && errno == EINTR) { + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + goto retry; + } else { + elapsed = (PRIntervalTime) (PR_IntervalNow() - start); + if (elapsed > timeout) { + rv = 0; /* timed out */ + } else { + remaining = timeout - elapsed; + tv.tv_sec = (PRInt32)PR_IntervalToSeconds(remaining); + tv.tv_usec = (PRInt32)PR_IntervalToMicroseconds( + remaining - PR_SecondsToInterval(tv.tv_sec)); + goto retry; + } + } + } + + if (rv > 0) { + _PR_setset(pr_rd, &rd); + _PR_setset(pr_wr, &wr); + _PR_setset(pr_ex, &ex); + } else if (rv == -1) { + pt_MapError(_PR_MD_MAP_SELECT_ERROR, errno); + } + return rv; +} + +#endif /* defined(_PR_PTHREADS) */ + +/* ptio.c */ diff --git a/pr/src/pthreads/ptmisc.c b/pr/src/pthreads/ptmisc.c new file mode 100644 index 00000000..669b7822 --- /dev/null +++ b/pr/src/pthreads/ptmisc.c @@ -0,0 +1,45 @@ +/* -*- 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. + */ + +/* +** File: ptmisc.c +** Descritpion: Implemenation of miscellaneous methods for pthreads +*/ + +#if defined(_PR_PTHREADS) + +#include "primpl.h" + +#include <stdio.h> + +#define PT_LOG(f) + +void _PR_InitCPUs(void) {PT_LOG("_PR_InitCPUs")} +void _MD_StartInterrupts(void) {PT_LOG("_MD_StartInterrupts")} +void _PR_InitStacks(void) {PT_LOG("_PR_InitStacks")} +void _PR_InitTPD(void) {PT_LOG("_PR_InitTPD")} + +PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) + {PT_LOG("PR_SetConcurrency")} + +PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 flag) + {PT_LOG("PR_SetThreadRecycleMode")} + +#endif /* defined(_PR_PTHREADS) */ + +/* ptmisc.c */ diff --git a/pr/src/pthreads/ptsynch.c b/pr/src/pthreads/ptsynch.c new file mode 100644 index 00000000..8906cda3 --- /dev/null +++ b/pr/src/pthreads/ptsynch.c @@ -0,0 +1,702 @@ +/* -*- 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. + */ + +/* +** File: ptsynch.c +** Descritpion: Implemenation for thread synchronization using pthreads +** Exports: prlock.h, prcvar.h, prmon.h, prcmon.h +*/ + +#if defined(_PR_PTHREADS) + +#include "primpl.h" +#include "obsolete/prsem.h" + +#include <string.h> +#include <pthread.h> +#include <sys/time.h> + +static pthread_condattr_t _pt_cvar_attr; + +#if defined(DEBUG) && defined(_PR_DCETHREADS) +static pthread_t pt_zero_tid; /* a null pthread_t (pthread_t is a struct + * in DCE threads) to compare with */ +#endif + +/**************************************************************/ +/**************************************************************/ +/*****************************LOCKS****************************/ +/**************************************************************/ +/**************************************************************/ + +void _PR_InitLocks(void) +{ + int rv = PTHREAD_CONDATTR_INIT(&_pt_cvar_attr); + PR_ASSERT(0 == rv); + _PR_MD_INIT_LOCKS(); +} + +static void pt_PostNotifies(PRLock *lock, PRBool unlock) +{ + PRIntn index, rv; + _PT_Notified post; + _PT_Notified *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) + memset(&lock->notified, 0, sizeof(_PT_Notified)); /* reset */ +#else + lock->notified.length = 0; /* these are really sufficient */ + lock->notified.link = NULL; +#endif + + /* should (may) we release lock before notifying? */ + if (unlock) + { + rv = pthread_mutex_unlock(&lock->mutex); + PR_ASSERT(0 == rv); + } + + notified = &post; /* this is where we start */ + do + { + for (index = 0; index < notified->length; ++index) + { + PR_ASSERT(NULL != notified->cv[index].cv); + PR_ASSERT(0 != notified->cv[index].times); + if (-1 == notified->cv[index].times) + { + rv = pthread_cond_broadcast(¬ified->cv[index].cv->cv); + PR_ASSERT(0 == rv); + } + else + { + while (notified->cv[index].times-- > 0) + { + rv = pthread_cond_signal(¬ified->cv[index].cv->cv); + PR_ASSERT(0 == rv); + } + } + } + prev = notified; + notified = notified->link; + if (&post != prev) PR_DELETE(prev); + } while (NULL != notified); +} /* pt_PostNotifies */ + +PR_IMPLEMENT(PRLock*) PR_NewLock(void) +{ + PRIntn rv; + PRLock *lock; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + lock = PR_NEWZAP(PRLock); + if (lock != NULL) + { + pthread_mutexattr_t mattr; + rv = PTHREAD_MUTEXATTR_INIT(&mattr); + PR_ASSERT(0 == rv); + rv = PTHREAD_MUTEX_INIT(lock->mutex, mattr); + PR_ASSERT(0 == rv); + rv = PTHREAD_MUTEXATTR_DESTROY(&mattr); + PR_ASSERT(0 == rv); + } + return lock; +} /* PR_NewLock */ + +PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) +{ + PRIntn rv; + PR_ASSERT(NULL != lock); + PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner)); + PR_ASSERT(0 == lock->notified.length); + PR_ASSERT(NULL == lock->notified.link); + rv = pthread_mutex_destroy(&lock->mutex); + PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(lock, 0xaf, sizeof(PRLock)); +#endif + PR_DELETE(lock); +} /* PR_DestroyLock */ + +PR_IMPLEMENT(void) PR_Lock(PRLock *lock) +{ + PRIntn rv; + PR_ASSERT(lock != NULL); + rv = pthread_mutex_lock(&lock->mutex); + PR_ASSERT(0 == rv); + PR_ASSERT(0 == lock->notified.length); + PR_ASSERT(NULL == lock->notified.link); + PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner)); + PTHREAD_COPY_THR_HANDLE(pthread_self(), lock->owner); +} /* PR_Lock */ + +PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) +{ + PRIntn rv; + + PR_ASSERT(lock != NULL); + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(lock->mutex)); + PR_ASSERT(pthread_equal(lock->owner, pthread_self())); + + if (!pthread_equal(lock->owner, pthread_self())) + return PR_FAILURE; + + PTHREAD_ZERO_THR_HANDLE(lock->owner); + if (0 == lock->notified.length) /* shortcut */ + { + rv = pthread_mutex_unlock(&lock->mutex); + PR_ASSERT(0 == rv); + } + else pt_PostNotifies(lock, PR_TRUE); + + return PR_SUCCESS; +} /* PR_Unlock */ + + +/**************************************************************/ +/**************************************************************/ +/***************************CONDITIONS*************************/ +/**************************************************************/ +/**************************************************************/ + + +/* + * This code is used to compute the absolute time for the wakeup. + * It's moderately ugly, so it's defined here and called in a + * couple of places. + */ +#define PT_NANOPERMICRO 1000UL +#define PT_BILLION 1000000000UL + +static PRIntn pt_TimedWait( + pthread_cond_t *cv, pthread_mutex_t *ml, PRIntervalTime timeout) +{ + int rv; + struct timeval now; + struct timespec tmo; + PRUint32 ticks = PR_TicksPerSecond(); + + tmo.tv_sec = (PRInt32)(timeout / ticks); + tmo.tv_nsec = (PRInt32)(timeout - (tmo.tv_sec * ticks)); + tmo.tv_nsec = (PRInt32)PR_IntervalToMicroseconds(PT_NANOPERMICRO * tmo.tv_nsec); + + /* pthreads wants this in absolute time, off we go ... */ +#if defined(SOLARIS) && defined(_SVID_GETTOD) + (void)gettimeofday(&now); +#else + (void)gettimeofday(&now, NULL); +#endif + /* that one's usecs, this one's nsecs - grrrr! */ + tmo.tv_sec += now.tv_sec; + tmo.tv_nsec += (PT_NANOPERMICRO * now.tv_usec); + tmo.tv_sec += tmo.tv_nsec / PT_BILLION; + tmo.tv_nsec %= PT_BILLION; + + rv = pthread_cond_timedwait(cv, ml, &tmo); + + /* NSPR doesn't report timeouts */ +#ifdef _PR_DCETHREADS + return (rv == -1 && errno == EAGAIN) ? 0 : rv; +#else + return (rv == ETIMEDOUT) ? 0 : rv; +#endif +} /* pt_TimedWait */ + + +/* + * Notifies just get posted to the 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 pt_PostNotifyToCvar(PRCondVar *cvar, PRBool broadcast) +{ + PRIntn index = 0; + _PT_Notified *notified = &cvar->lock->notified; + + PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); + + 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; + goto finished; /* we're finished */ + } + } + /* if not full, enter new CV in this array */ + if (notified->length < PT_CV_NOTIFIED_LENGTH) break; + + /* if there's no link, create an empty array and link it */ + if (NULL == notified->link) + notified->link = PR_NEWZAP(_PT_Notified); + 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; + +finished: + PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); +} /* pt_PostNotifyToCvar */ + +PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock) +{ + PRCondVar *cv = PR_NEW(PRCondVar); + PR_ASSERT(lock != NULL); + if (cv != NULL) + { + int rv = PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); + PR_ASSERT(0 == rv); + cv->lock = lock; + } + return cv; +} /* PR_NewCondVar */ + +PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar) +{ + int rv; + rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(cvar, 0xaf, sizeof(PRCondVar)); +#endif + PR_DELETE(cvar); +} /* PR_DestroyCondVar */ + +PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) +{ + PRIntn rv; + PRThread *thred = PR_CurrentThread(); + + PR_ASSERT(cvar != NULL); + /* We'd better be locked */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(cvar->lock->mutex)); + /* and it better be by us */ + PR_ASSERT(pthread_equal(cvar->lock->owner, pthread_self())); + + if (thred->state & PT_THREAD_ABORTED) goto aborted; + + /* + * The thread waiting is used for PR_Interrupt + */ + thred->waiting = cvar; /* this is where we're waiting */ + + /* + * If we have pending notifies, post them now. + * + * This is not optimal. We're going to post these notifies + * while we're holding the lock. That means on MP systems + * that they are going to collide for the lock that we will + * hold until we actually wait. + */ + if (0 != cvar->lock->notified.length) + pt_PostNotifies(cvar->lock, PR_FALSE); + + /* + * We're surrendering the lock, so clear out the owner field. + */ + PTHREAD_ZERO_THR_HANDLE(cvar->lock->owner); + + if (timeout == PR_INTERVAL_NO_TIMEOUT) + rv = pthread_cond_wait(&cvar->cv, &cvar->lock->mutex); + else + rv = pt_TimedWait(&cvar->cv, &cvar->lock->mutex, timeout); + + /* We just got the lock back - this better be empty */ + PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(cvar->lock->owner)); + PTHREAD_COPY_THR_HANDLE(pthread_self(), cvar->lock->owner); + + PR_ASSERT(0 == cvar->lock->notified.length); + thred->waiting = NULL; /* and now we're not */ + if (thred->state & PT_THREAD_ABORTED) goto aborted; + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; + +aborted: + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thred->state &= ~PT_THREAD_ABORTED; + return PR_FAILURE; +} /* PR_WaitCondVar */ + +PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar != NULL); + pt_PostNotifyToCvar(cvar, PR_FALSE); + return PR_SUCCESS; +} /* PR_NotifyCondVar */ + +PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar != NULL); + pt_PostNotifyToCvar(cvar, PR_TRUE); + return PR_SUCCESS; +} /* PR_NotifyAllCondVar */ + +/**************************************************************/ +/**************************************************************/ +/***************************MONITORS***************************/ +/**************************************************************/ +/**************************************************************/ + +PR_IMPLEMENT(PRMonitor*) PR_NewMonitor(void) +{ + PRMonitor *ml; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + ml = PR_NEWZAP(PRMonitor); + if (ml != NULL) + { + int rv; + pthread_mutexattr_t mattr; + rv = PTHREAD_MUTEXATTR_INIT(&mattr); + PR_ASSERT(0 == rv); + rv += PTHREAD_MUTEX_INIT(ml->lock.mutex, mattr); + PR_ASSERT(0 == rv); + rv += PTHREAD_MUTEXATTR_DESTROY(&mattr); + PR_ASSERT(0 == rv); + + rv += PTHREAD_COND_INIT(ml->cvar.cv, _pt_cvar_attr); + PR_ASSERT(0 == rv); + ml->entryCount = 0; + ml->cvar.lock = &ml->lock; + if (0 != rv) + { + PR_DELETE(ml); + ml = NULL; + } + } + return ml; +} /* PR_NewMonitor */ + +PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) +{ + PRMonitor* mon = PR_NewMonitor(); + mon->name = name; + return mon; +} + +PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) +{ + int rv; + PR_ASSERT(mon != NULL); + rv = pthread_cond_destroy(&mon->cvar.cv); PR_ASSERT(0 == rv); + rv = pthread_mutex_destroy(&mon->lock.mutex); PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(mon, 0xaf, sizeof(PRMonitor)); +#endif + PR_DELETE(mon); +} /* PR_DestroyMonitor */ + + +/* The GC uses this; it is quite arguably a bad interface. I'm just + * duplicating it for now - XXXMB + */ +PR_IMPLEMENT(PRInt32) PR_GetMonitorEntryCount(PRMonitor *mon) +{ + pthread_t self = pthread_self(); + if (pthread_equal(mon->owner, self)) + return mon->entryCount; + return 0; +} + +PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) +{ + int rv; + pthread_t self = pthread_self(); + + PR_ASSERT(mon != NULL); + rv = pthread_mutex_trylock(&mon->lock.mutex); +#ifdef _PR_DCETHREADS + if (1 == rv) +#else + if (0 == rv) +#endif + { + /* I now have the lock - I can play in the sandbox */ + /* could/should/would not have gotten lock if entries != 0 */ + PR_ASSERT(0 == mon->entryCount); + PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(mon->lock.owner)); + PTHREAD_COPY_THR_HANDLE(pthread_self(), mon->lock.owner); + PTHREAD_COPY_THR_HANDLE(self, mon->owner); + } + else + { + PR_ASSERT(PT_TRYLOCK_BUSY == rv); /* and if it isn't? */ + /* somebody has it locked - is it me? */ + if (!pthread_equal(mon->owner, self)) + { + /* it's not me - this should block */ + PR_Lock(&mon->lock); + /* and now I have the lock */ + PR_ASSERT(0 == mon->entryCount); + PTHREAD_COPY_THR_HANDLE(self, mon->owner); + } + } + mon->entryCount += 1; +} /* PR_EnterMonitor */ + +PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) +{ + pthread_t self = pthread_self(); + + PR_ASSERT(mon != NULL); + /* The lock better be that - locked */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex)); + /* we'd better be the owner */ + PR_ASSERT(pthread_equal(mon->owner, self)); + if (!pthread_equal(mon->owner, self)) + return PR_FAILURE; + + /* if it's locked and we have it, then the entries should be > 0 */ + PR_ASSERT(mon->entryCount > 0); + mon->entryCount -= 1; /* reduce by one */ + if (mon->entryCount == 0) + { + /* and if it transitioned to zero - unlock */ + PTHREAD_ZERO_THR_HANDLE(mon->owner); /* make the owner unknown */ + PR_Unlock(&mon->lock); + } + return PR_SUCCESS; +} /* PR_ExitMonitor */ + +PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime timeout) +{ + PRStatus rv; + PRInt16 saved_entries; + pthread_t saved_owner; + + PR_ASSERT(mon != NULL); + /* we'd better be locked */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex)); + /* and the entries better be positive */ + PR_ASSERT(mon->entryCount > 0); + /* and it better be by us */ + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); + + /* tuck these away 'till later */ + saved_entries = mon->entryCount; + mon->entryCount = 0; + PTHREAD_COPY_THR_HANDLE(mon->owner, saved_owner); + PTHREAD_ZERO_THR_HANDLE(mon->owner); + + rv = PR_WaitCondVar(&mon->cvar, timeout); + + /* reinstate the intresting information */ + mon->entryCount = saved_entries; + PTHREAD_COPY_THR_HANDLE(saved_owner, mon->owner); + + return rv; +} /* PR_Wait */ + +PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) +{ + PR_ASSERT(NULL != mon); + /* we'd better be locked */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex)); + /* and the entries better be positive */ + PR_ASSERT(mon->entryCount > 0); + /* and it better be by us */ + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); + + pt_PostNotifyToCvar(&mon->cvar, PR_FALSE); + + return PR_SUCCESS; +} /* PR_Notify */ + +PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) +{ + PR_ASSERT(mon != NULL); + /* we'd better be locked */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(mon->lock.mutex)); + /* and the entries better be positive */ + PR_ASSERT(mon->entryCount > 0); + /* and it better be by us */ + PR_ASSERT(pthread_equal(mon->owner, pthread_self())); + + pt_PostNotifyToCvar(&mon->cvar, PR_TRUE); + + return PR_SUCCESS; +} /* PR_NotifyAll */ + +/**************************************************************/ +/**************************************************************/ +/**************************SEMAPHORES**************************/ +/**************************************************************/ +/**************************************************************/ +PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *semaphore) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_PostSem", "locks & condition variables"); + PR_Lock(semaphore->cvar->lock); + PR_NotifyCondVar(semaphore->cvar); + semaphore->count += 1; + PR_Unlock(semaphore->cvar->lock); +} /* PR_PostSem */ + +PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *semaphore) +{ + PRStatus status = PR_SUCCESS; + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_WaitSem", "locks & condition variables"); + PR_Lock(semaphore->cvar->lock); + while ((semaphore->count == 0) && (PR_SUCCESS == status)) + status = PR_WaitCondVar(semaphore->cvar, PR_INTERVAL_NO_TIMEOUT); + if (PR_SUCCESS == status) semaphore->count -= 1; + PR_Unlock(semaphore->cvar->lock); + return status; +} /* PR_WaitSem */ + +PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *semaphore) +{ + static PRBool unwarned = PR_TRUE; + if (unwarned) unwarned = _PR_Obsolete( + "PR_DestroySem", "locks & condition variables"); + PR_DestroyLock(semaphore->cvar->lock); + PR_DestroyCondVar(semaphore->cvar); + PR_DELETE(semaphore); +} /* PR_DestroySem */ + +PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) +{ + PRSemaphore *semaphore; + static PRBool unwarned = PR_TRUE; + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (unwarned) unwarned = _PR_Obsolete( + "PR_NewSem", "locks & condition variables"); + + semaphore = PR_NEWZAP(PRSemaphore); + if (NULL != semaphore) + { + PRLock *lock = PR_NewLock(); + if (NULL != lock) + { + semaphore->cvar = PR_NewCondVar(lock); + if (NULL != semaphore->cvar) + { + semaphore->count = value; + return semaphore; + } + PR_DestroyLock(lock); + } + PR_DELETE(semaphore); + } + return NULL; +} + +/**************************************************************/ +/**************************************************************/ +/******************ROUTINES FOR DCE EMULATION******************/ +/**************************************************************/ +/**************************************************************/ + +#include "prpdce.h" + +PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) +{ + PRIntn rv = pthread_mutex_trylock(&lock->mutex); + if (rv == PT_TRYLOCK_SUCCESS) + { + PR_ASSERT(PTHREAD_THR_HANDLE_IS_ZERO(lock->owner)); + PTHREAD_COPY_THR_HANDLE(pthread_self(), lock->owner); + } + else + PR_ASSERT(!PTHREAD_THR_HANDLE_IS_ZERO(lock->owner)); + /* XXX set error code? */ + return (PT_TRYLOCK_SUCCESS == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PRP_TryLock */ + +PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar() +{ + PRCondVar *cv; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + cv = PR_NEW(PRCondVar); + if (cv != NULL) + { + int rv; + rv = PTHREAD_COND_INIT(cv->cv, _pt_cvar_attr); + PR_ASSERT(0 == rv); + cv->lock = _PR_NAKED_CV_LOCK; + } + return cv; +} /* PRP_NewNakedCondVar */ + +PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar) +{ + int rv; + rv = pthread_cond_destroy(&cvar->cv); PR_ASSERT(0 == rv); +#if defined(DEBUG) + memset(cvar, 0xaf, sizeof(PRCondVar)); +#endif + PR_DELETE(cvar); +} /* PRP_DestroyNakedCondVar */ + +PR_IMPLEMENT(PRStatus) PRP_NakedWait( + PRCondVar *cvar, PRLock *ml, PRIntervalTime timeout) +{ + PRIntn rv; + PR_ASSERT(cvar != NULL); + /* XXX do we really want to assert this in a naked wait? */ + PR_ASSERT(PTHREAD_MUTEX_IS_LOCKED(ml->mutex)); + if (timeout == PR_INTERVAL_NO_TIMEOUT) + rv = pthread_cond_wait(&cvar->cv, &ml->mutex); + else + rv = pt_TimedWait(&cvar->cv, &ml->mutex, timeout); + return (rv == 0) ? PR_SUCCESS : PR_FAILURE; +} /* PRP_NakedWait */ + +PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar) +{ + int rv; + PR_ASSERT(cvar != NULL); + rv = pthread_cond_signal(&cvar->cv); + PR_ASSERT(0 == rv); + return PR_SUCCESS; +} /* PRP_NakedNotify */ + +PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) +{ + int rv; + PR_ASSERT(cvar != NULL); + rv = pthread_cond_broadcast(&cvar->cv); + PR_ASSERT(0 == rv); + return PR_SUCCESS; +} /* PRP_NakedBroadcast */ + +#endif /* defined(_PR_PTHREADS) */ + +/* ptsynch.c */ diff --git a/pr/src/pthreads/ptthread.c b/pr/src/pthreads/ptthread.c new file mode 100644 index 00000000..784ffb29 --- /dev/null +++ b/pr/src/pthreads/ptthread.c @@ -0,0 +1,1309 @@ +/* -*- 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. + */ + +/* +** File: ptthread.c +** Descritpion: Implemenation for threds using pthreds +** Exports: ptthread.h +*/ + +#if defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) + +#include "prlog.h" +#include "primpl.h" +#include "prpdce.h" + +#include <pthread.h> +#include <string.h> +#include <signal.h> + +/* + * Record whether or not we have the privilege to set the scheduling + * policy and priority of threads. 0 means that privilege is available. + * EPERM means that privilege is not available. + */ + +PRIntn pt_schedpriv; + +struct _PT_Bookeeping pt_book = {0}; + +static void init_pthread_gc_support(void); + +static PRIntn pt_PriorityMap(PRThreadPriority pri) +{ + return pt_book.minPrio + + pri * (pt_book.maxPrio - pt_book.minPrio) / PR_PRIORITY_LAST; +} + +/* +** Initialize a stack for a native pthread thread +*/ +static void _PR_InitializeStack(PRThreadStack *ts) +{ + if( ts && (ts->stackTop == 0) ) { + ts->allocBase = (char *) &ts; + ts->allocSize = ts->stackSize; + + /* + ** Setup stackTop and stackBottom values. + */ +#ifdef HAVE_STACK_GROWING_UP + ts->stackBottom = ts->allocBase + ts->stackSize; + ts->stackTop = ts->allocBase; +#else + ts->stackTop = ts->allocBase; + ts->stackBottom = ts->allocBase - ts->stackSize; +#endif + } +} + +static void *_pt_root(void *arg) +{ + PRIntn rv; + PRThread *thred = (PRThread*)arg; + PRBool detached = (thred->state & PT_THREAD_DETACHED) ? PR_TRUE : PR_FALSE; + + /* + * Both the parent thread and this new thread set thred->id. + * The new thread must ensure that thred->id is set before + * it executes its startFunc. The parent thread must ensure + * that thred->id is set before PR_CreateThread() returns. + * Both threads set thred->id without holding a lock. Since + * they are writing the same value, this unprotected double + * write should be safe. + */ + thred->id = pthread_self(); + + /* + ** DCE Threads can't detach during creation, so do it late. + ** I would like to do it only here, but that doesn't seem + ** to work. + */ +#if defined(_PR_DCETHREADS) + if (detached) + { + /* pthread_detach() modifies its argument, so we must pass a copy */ + pthread_t self = thred->id; + rv = pthread_detach(&self); + PR_ASSERT(0 == rv); + } +#endif /* defined(_PR_DCETHREADS) */ + + /* Set up the thread stack information */ + _PR_InitializeStack(thred->stack); + + /* + * Set within the current thread the pointer to our object. + * This object will be deleted when the thread termintates, + * whether in a join or detached (see _PR_InitThreads()). + */ + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); + + /* make the thread visible to the rest of the runtime */ + PR_Lock(pt_book.ml); + + /* If this is a GCABLE thread, set its state appropriately */ + if (thred->suspend & PT_THREAD_SETGCABLE) + thred->state |= PT_THREAD_GCABLE; + thred->suspend = 0; + + thred->prev = pt_book.last; + pt_book.last->next = thred; + thred->next = NULL; + pt_book.last = thred; + PR_Unlock(pt_book.ml); + + thred->startFunc(thred->arg); /* make visible to the client */ + + /* unhook the thread from the runtime */ + PR_Lock(pt_book.ml); + if (thred->state & PT_THREAD_SYSTEM) + pt_book.system -= 1; + else if (--pt_book.user == pt_book.this_many) + PR_NotifyAllCondVar(pt_book.cv); + thred->prev->next = thred->next; + if (NULL == thred->next) + pt_book.last = thred->prev; + else + thred->next->prev = thred->prev; + + /* + * At this moment, PR_CreateThread() may not have set thred->id yet. + * It is safe for a detached thread to free thred only after + * PR_CreateThread() has set thred->id. + */ + if (detached) + { + while (!thred->okToDelete) + PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(pt_book.ml); + + /* last chance to delete this puppy if the thread is detached */ + if (detached) + { + PR_DELETE(thred->stack); +#if defined(DEBUG) + memset(thred, 0xaf, sizeof(PRThread)); +#endif + PR_DELETE(thred); + } + + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + return NULL; +} /* _pt_root */ + +static PRThread* _PR_CreateThread( + PRThreadType type, void (*start)(void *arg), + void *arg, PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize, PRBool isGCAble) +{ + int rv; + PRThread *thred; + pthread_attr_t tattr; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) + priority = PR_PRIORITY_FIRST; + else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) + priority = PR_PRIORITY_LAST; + + rv = PTHREAD_ATTR_INIT(&tattr); + PR_ASSERT(0 == rv); + + if (EPERM != pt_schedpriv) + { +#if !defined(_PR_DCETHREADS) && !defined(FREEBSD) + struct sched_param schedule; +#endif + +#if !defined(FREEBSD) + rv = pthread_attr_setinheritsched(&tattr, PTHREAD_EXPLICIT_SCHED); + PR_ASSERT(0 == rv); +#endif + + /* Use the default scheduling policy */ + +#if defined(_PR_DCETHREADS) + rv = pthread_attr_setprio(&tattr, pt_PriorityMap(priority)); + PR_ASSERT(0 == rv); +#elif !defined(FREEBSD) + rv = pthread_attr_getschedparam(&tattr, &schedule); + PR_ASSERT(0 == rv); + schedule.sched_priority = pt_PriorityMap(priority); + rv = pthread_attr_setschedparam(&tattr, &schedule); + PR_ASSERT(0 == rv); +#endif /* !defined(_PR_DCETHREADS) */ + } + + /* + * DCE threads can't set detach state before creating the thread. + * AIX can't set detach late. Why can't we all just get along? + */ +#if !defined(_PR_DCETHREADS) + rv = pthread_attr_setdetachstate(&tattr, + ((PR_JOINABLE_THREAD == state) ? + PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED)); + PR_ASSERT(0 == rv); +#endif /* !defined(_PR_DCETHREADS) */ + + /* + * HPUX only supports PTHREAD_SCOPE_SYSTEM. + * IRIX only supports PTHREAD_SCOPE_PROCESS. + * OSF1 only supports PTHREAD_SCOPE_PROCESS. + * AIX only supports PTHREAD_SCOPE_SYSTEM. + */ +#if defined(SOLARIS) + rv = pthread_attr_setscope(&tattr, + ((PR_GLOBAL_THREAD == scope) ? + PTHREAD_SCOPE_SYSTEM : PTHREAD_SCOPE_PROCESS)); + PR_ASSERT(0 == rv); +#endif + +#if defined(IRIX) + if ((16 * 1024) > stackSize) stackSize = (16 * 1024); /* IRIX minimum */ + else +#endif + if (0 == stackSize) stackSize = (64 * 1024); /* default == 64K */ + /* + * Linux doesn't have pthread_attr_setstacksize. + */ +#ifndef LINUX + rv = pthread_attr_setstacksize(&tattr, stackSize); + PR_ASSERT(0 == rv); +#endif + + thred = PR_NEWZAP(PRThread); + if (thred != NULL) + { + pthread_t id; + + thred->arg = arg; + thred->startFunc = start; + thred->priority = priority; + if (PR_UNJOINABLE_THREAD == state) + thred->state |= PT_THREAD_DETACHED; + if (PR_GLOBAL_THREAD == scope) + thred->state |= PT_THREAD_GLOBAL; + if (PR_SYSTEM_THREAD == type) + thred->state |= PT_THREAD_SYSTEM; + + thred->suspend =(isGCAble) ? PT_THREAD_SETGCABLE : 0; + + thred->stack = PR_NEWZAP(PRThreadStack); + if (thred->stack == NULL) { + PR_DELETE(thred); /* all that work ... poof! */ + thred = NULL; /* and for what? */ + goto done; + } + thred->stack->stackSize = stackSize; + thred->stack->thr = thred; + +#ifdef PT_NO_SIGTIMEDWAIT + pthread_mutex_init(&thred->suspendResumeMutex,NULL); + pthread_cond_init(&thred->suspendResumeCV,NULL); +#endif + + /* make the thread counted to the rest of the runtime */ + PR_Lock(pt_book.ml); + if (thred->state & PT_THREAD_SYSTEM) + pt_book.system += 1; + else pt_book.user += 1; + PR_Unlock(pt_book.ml); + + /* + * We pass a pointer to a local copy (instead of thred->id) + * to pthread_create() because who knows what wacky things + * pthread_create() may be doing to its argument. + */ + if (PTHREAD_CREATE(&id, tattr, _pt_root, thred) != 0) + { + PR_Lock(pt_book.ml); + if (thred->state & PT_THREAD_SYSTEM) + pt_book.system -= 1; + else if (--pt_book.user == pt_book.this_many) + PR_NotifyAllCondVar(pt_book.cv); + PR_Unlock(pt_book.ml); + + PR_DELETE(thred->stack); + PR_DELETE(thred); /* all that work ... poof! */ + thred = NULL; /* and for what? */ + goto done; + } + + /* + * Both the parent thread and this new thread set thred->id. + * The parent thread must ensure that thred->id is set before + * PR_CreateThread() returns. (See comments in _pt_root().) + */ + thred->id = id; + + /* + * If the new thread is detached, tell it that PR_CreateThread() + * has set thred->id so it's ok to delete thred. + */ + if (PR_UNJOINABLE_THREAD == state) + { + PR_Lock(pt_book.ml); + thred->okToDelete = PR_TRUE; + PR_NotifyAllCondVar(pt_book.cv); + PR_Unlock(pt_book.ml); + } + } + +done: + rv = PTHREAD_ATTR_DESTROY(&tattr); + PR_ASSERT(0 == rv); + + return thred; +} /* _PR_CreateThread */ + +PR_IMPLEMENT(PRThread*) PR_CreateThread( + PRThreadType type, void (*start)(void *arg), void *arg, + PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize) +{ + return _PR_CreateThread( + type, start, arg, priority, scope, state, stackSize, PR_FALSE); +} /* PR_CreateThread */ + +PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble( + PRThreadType type, void (*start)(void *arg), void *arg, + PRThreadPriority priority, PRThreadScope scope, + PRThreadState state, PRUint32 stackSize) +{ + return _PR_CreateThread( + type, start, arg, priority, scope, state, stackSize, PR_TRUE); +} /* PR_CreateThreadGCAble */ + +PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thred) +{ + return thred->environment; +} /* GetExecutionEnvironment */ + +PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thred, void *env) +{ + thred->environment = env; +} /* SetExecutionEnvironment */ + +PR_IMPLEMENT(PRThread*) PR_AttachThread( + PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) +{ + PRThread *thred = NULL; + void *privateData = NULL; + + /* + * NSPR must have been initialized when PR_AttachThread is called. + * We cannot have PR_AttachThread call implicit initialization + * because if multiple threads call PR_AttachThread simultaneously, + * NSPR may be initialized more than once. + */ + if (!_pr_initialized) { + /* Since NSPR is not initialized, we cannot call PR_SetError. */ + return NULL; + } + + PTHREAD_GETSPECIFIC(pt_book.key, privateData); + if (NULL != privateData) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return NULL; + } + thred = PR_NEWZAP(PRThread); + if (NULL == thred) PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + else + { + int rv; + + if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)priority) + priority = PR_PRIORITY_FIRST; + else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)priority) + priority = PR_PRIORITY_LAST; + + thred->priority = priority; + thred->id = pthread_self(); + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); + + PR_Lock(pt_book.ml); + if (PR_SYSTEM_THREAD == type) + { + pt_book.system += 1; + thred->state |= PT_THREAD_SYSTEM; + } + else pt_book.user += 1; + + /* then put it into the list */ + thred->prev = pt_book.last; + pt_book.last->next = thred; + thred->next = NULL; + pt_book.last = thred; + PR_Unlock(pt_book.ml); + + } + return thred; /* may be NULL */ +} /* PR_AttachThread */ + +PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thred) +{ + int rv = -1; + void *result = NULL; + PR_ASSERT(thred != NULL); + + if ((0xafafafaf == thred->state) + || (PT_THREAD_DETACHED & thred->state)) + { + /* + * This might be a bad address, but if it isn't, the state should + * either be an unjoinable thread or it's already had the object + * deleted. However, the client that called join on a detached + * thread deserves all the rath I can muster.... + */ + PR_SetError(PR_ILLEGAL_ACCESS_ERROR, 0); + PR_LogPrint( + "PR_JoinThread: 0x%X not joinable | already smashed\n", thred); + + PR_ASSERT((0xafafafaf == thred->state) + || (PT_THREAD_DETACHED & thred->state)); + } + else + { + pthread_t id = thred->id; + rv = pthread_join(id, &result); + if (0 != rv) + PR_SetError(PR_UNKNOWN_ERROR, errno); + PR_DELETE(thred->stack); + memset(thred, 0xaf, sizeof(PRThread)); + PR_ASSERT(result == NULL); + PR_DELETE(thred); + } + return (0 == rv) ? PR_SUCCESS : PR_FAILURE; +} /* PR_JoinThread */ + +PR_IMPLEMENT(void) PR_DetachThread() +{ + void *tmp; + PTHREAD_GETSPECIFIC(pt_book.key, tmp); + PR_ASSERT(NULL != tmp); + + if (NULL != tmp) + { + int rv; + PRThread *thred = (PRThread*)tmp; + + PR_Lock(pt_book.ml); + if (thred->state & PT_THREAD_SYSTEM) + pt_book.system -= 1; + else if (--pt_book.user == pt_book.this_many) + PR_NotifyAllCondVar(pt_book.cv); + thred->prev->next = thred->next; + if (NULL == thred->next) + pt_book.last = thred->prev; + else + thred->next->prev = thred->prev; + PR_Unlock(pt_book.ml); + + rv = pthread_setspecific(pt_book.key, NULL); + PR_ASSERT(0 == rv); + PR_DELETE(thred->stack); + memset(thred, 0xaf, sizeof(PRThread)); + PR_DELETE(thred); + } +} /* PR_DetachThread */ + +PR_IMPLEMENT(PRThread*) PR_GetCurrentThread() +{ + void *thred; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + PTHREAD_GETSPECIFIC(pt_book.key, thred); + PR_ASSERT(NULL != thred); + return (PRThread*)thred; +} /* PR_GetCurrentThread */ + +PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thred) +{ + return (thred->state & PT_THREAD_GLOBAL) ? + PR_GLOBAL_THREAD : PR_LOCAL_THREAD; +} /* PR_GetThreadScope() */ + +PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thred) +{ + return (thred->state & PT_THREAD_SYSTEM) ? + PR_SYSTEM_THREAD : PR_USER_THREAD; +} + +PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thred) +{ + return (thred->state & PT_THREAD_DETACHED) ? + PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; +} /* PR_GetThreadState */ + +PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thred) +{ + PR_ASSERT(thred != NULL); + return thred->priority; +} /* PR_GetThreadPriority */ + +PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thred, PRThreadPriority newPri) +{ + PRIntn rv; + + PR_ASSERT(NULL != thred); + + if ((PRIntn)PR_PRIORITY_FIRST > (PRIntn)newPri) + newPri = PR_PRIORITY_FIRST; + else if ((PRIntn)PR_PRIORITY_LAST < (PRIntn)newPri) + newPri = PR_PRIORITY_LAST; + +#if defined(_PR_DCETHREADS) + rv = pthread_setprio(thred->id, pt_PriorityMap(newPri)); + /* pthread_setprio returns the old priority */ + PR_ASSERT(-1 != rv); +#elif !defined(FREEBSD) + if (EPERM != pt_schedpriv) + { + int policy; + struct sched_param schedule; + + rv = pthread_getschedparam(thred->id, &policy, &schedule); + PR_ASSERT(0 == rv); + schedule.sched_priority = pt_PriorityMap(newPri); + rv = pthread_setschedparam(thred->id, policy, &schedule); + PR_ASSERT(0 == rv); + } +#endif + + thred->priority = newPri; +} /* PR_SetThreadPriority */ + +PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex( + PRUintn *newIndex, PRThreadPrivateDTOR destructor) +{ + int rv; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + rv = PTHREAD_KEY_CREATE((pthread_key_t*)newIndex, destructor); + + if (0 == rv) + { + PR_Lock(pt_book.ml); + if (*newIndex >= pt_book.highwater) + pt_book.highwater = *newIndex + 1; + PR_Unlock(pt_book.ml); + return PR_SUCCESS; + } + PR_SetError(PR_UNKNOWN_ERROR, rv); + return PR_FAILURE; +} /* PR_NewThreadPrivateIndex */ + +PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) +{ + PRIntn rv; + if ((pthread_key_t)index >= pt_book.highwater) + { + PR_SetError(PR_TPD_RANGE_ERROR, 0); + return PR_FAILURE; + } + rv = pthread_setspecific((pthread_key_t)index, priv); + PR_ASSERT(0 == rv); + if (0 == rv) return PR_SUCCESS; + + PR_SetError(PR_UNKNOWN_ERROR, rv); + return PR_FAILURE; +} /* PR_SetThreadPrivate */ + +PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn index) +{ + void *result = NULL; + if ((pthread_key_t)index < pt_book.highwater) + PTHREAD_GETSPECIFIC((pthread_key_t)index, result); + return result; +} /* PR_GetThreadPrivate */ + +PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thred) +{ + /* + ** If the target thread indicates that it's waiting, + ** find the condition and broadcast to it. Broadcast + ** since we don't know which thread (if there are more + ** than one). This sounds risky, but clients must + ** test their invariants when resumed from a wait and + ** I don't expect very many threads to be waiting on + ** a single condition and I don't expect interrupt to + ** be used very often. + */ + PRCondVar *victim; + PR_ASSERT(thred != NULL); + thred->state |= PT_THREAD_ABORTED; + victim = thred->waiting; + if (NULL != victim) + { + PRIntn haveLock = pthread_equal(victim->lock->owner, pthread_self()); + if (!haveLock) PR_Lock(victim->lock); + PR_NotifyAllCondVar(victim); + if (!haveLock) PR_Unlock(victim->lock); + } + return PR_SUCCESS; +} /* PR_Interrupt */ + +PR_IMPLEMENT(void) PR_ClearInterrupt() +{ + PRThread *me = PR_CurrentThread(); + me->state &= ~PT_THREAD_ABORTED; +} /* PR_ClearInterrupt */ + +PR_IMPLEMENT(PRStatus) PR_Yield() +{ + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( + "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); + return PR_Sleep(PR_INTERVAL_NO_WAIT); +} + +PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime ticks) +{ + PRStatus rv; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (PR_INTERVAL_NO_WAIT == ticks) + { + PTHREAD_YIELD(); + rv = PR_SUCCESS; + } + else + { + PRCondVar *cv = PR_NewCondVar(pt_book.ml); + PR_ASSERT(cv != NULL); + PR_Lock(pt_book.ml); + rv = PR_WaitCondVar(cv, ticks); + PR_Unlock(pt_book.ml); + PR_DestroyCondVar(cv); + } + return rv; +} /* PR_Sleep */ + +void _PR_InitThreads( + PRThreadType type, PRThreadPriority priority, PRUintn maxPTDs) +{ + int rv; + PRThread *thred; + + /* + ** These might be function evaluations + */ + pt_book.minPrio = PT_PRIO_MIN; + pt_book.maxPrio = PT_PRIO_MAX; + + PR_ASSERT(NULL == pt_book.ml); + pt_book.ml = PR_NewLock(); + PR_ASSERT(NULL != pt_book.ml); + pt_book.cv = PR_NewCondVar(pt_book.ml); + PR_ASSERT(NULL != pt_book.cv); + thred = PR_NEWZAP(PRThread); + PR_ASSERT(NULL != thred); + thred->arg = NULL; + thred->startFunc = NULL; + thred->priority = priority; + thred->id = pthread_self(); + + thred->state |= (PT_THREAD_DETACHED | PT_THREAD_PRIMORD); + if (PR_SYSTEM_THREAD == type) + { + thred->state |= PT_THREAD_SYSTEM; + pt_book.system += 1; + pt_book.this_many = 0; + } + else + { + pt_book.user += 1; + pt_book.this_many = 1; + } + thred->next = thred->prev = NULL; + pt_book.first = pt_book.last = thred; + + thred->stack = PR_NEWZAP(PRThreadStack); + PR_ASSERT(thred->stack != NULL); + thred->stack->stackSize = 0; + thred->stack->thr = thred; + _PR_InitializeStack(thred->stack); + + /* + * Create a key for our use to store a backpointer in the pthread + * to our PRThread object. This object gets deleted when the thread + * returns from its root in the case of a detached thread. Other + * threads delete the objects in Join. + * + * NB: The destructor logic seems to have a bug so it isn't used. + */ + rv = PTHREAD_KEY_CREATE(&pt_book.key, NULL); + PR_ASSERT(0 == rv); + rv = pthread_setspecific(pt_book.key, thred); + PR_ASSERT(0 == rv); + + pt_schedpriv = PT_PRIVCHECK(); + PR_SetThreadPriority(thred, priority); + + /* + * Linux pthreads use SIGUSR1 and SIGUSR2 internally, which + * conflict with the use of these two signals in our GC support. + * So we don't know how to support GC on Linux pthreads. + */ +#if !defined(LINUX2_0) && !defined(FREEBSD) + init_pthread_gc_support(); +#endif + +} /* _PR_InitThreads */ + +PR_IMPLEMENT(PRStatus) PR_Cleanup() +{ + PRThread *me = PR_CurrentThread(); + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("PR_Cleanup: shutting down NSPR")); + PR_ASSERT(me->state & PT_THREAD_PRIMORD); + if (me->state & PT_THREAD_PRIMORD) + { + PR_Lock(pt_book.ml); + while (pt_book.user > pt_book.this_many) + PR_WaitCondVar(pt_book.cv, PR_INTERVAL_NO_TIMEOUT); + PR_Unlock(pt_book.ml); + + /* + * I am not sure if it's safe to delete the cv and lock here, + * since there may still be "system" threads around. If this + * call isn't immediately prior to exiting, then there's a + * problem. + */ + if (0 == pt_book.system) + { + PR_DestroyCondVar(pt_book.cv); pt_book.cv = NULL; + PR_DestroyLock(pt_book.ml); pt_book.ml = NULL; + } + PR_DELETE(me->stack); + PR_DELETE(me); + return PR_SUCCESS; + } + return PR_FAILURE; +} /* PR_Cleanup */ + +PR_IMPLEMENT(void) PR_ProcessExit(PRIntn status) +{ + _exit(status); +} + +/* + * $$$ + * The following two thread-to-processor affinity functions are not + * yet implemented for pthreads. By the way, these functions should return + * PRStatus rather than PRInt32 to indicate the success/failure status. + * $$$ + */ + +PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) +{ + return 0; /* not implemented */ +} + +PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) +{ + return 0; /* not implemented */ +} + +PR_IMPLEMENT(void) +PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) +{ + thread->dump = dump; + thread->dumpArg = arg; +} + +/* + * Garbage collection support follows. + */ + +#if defined(_PR_DCETHREADS) + +/* + * statics for Garbage Collection support. We don't need to protect these + * signal masks since the garbage collector itself is protected by a lock + * and multiple threads will not be garbage collecting at the same time. + */ +static sigset_t javagc_vtalarm_sigmask; +static sigset_t javagc_intsoff_sigmask; + +#else /* defined(_PR_DCETHREADS) */ + +/* a bogus signal mask for forcing a timed wait */ +/* Not so bogus in AIX as we really do a sigwait */ +static sigset_t sigwait_set; + +static struct timespec onemillisec = {0, 1000000L}; +static struct timespec hundredmillisec = {0, 100000000L}; + +#endif /* defined(_PR_DCETHREADS) */ + +static void suspend_signal_handler(PRIntn sig); + +#ifdef PT_NO_SIGTIMEDWAIT +static void null_signal_handler(PRIntn sig); +#endif + +static void init_pthread_gc_support() +{ + PRIntn rv; + +#if defined(_PR_DCETHREADS) + rv = sigemptyset(&javagc_vtalarm_sigmask); + PR_ASSERT(0 == rv); + rv = sigaddset(&javagc_vtalarm_sigmask, SIGVTALRM); + PR_ASSERT(0 == rv); +#else /* defined(_PR_DCETHREADS) */ + { + struct sigaction sigact_usr2 = {0}; + + sigact_usr2.sa_handler = suspend_signal_handler; + sigact_usr2.sa_flags = SA_RESTART; + sigemptyset (&sigact_usr2.sa_mask); + + rv = sigaction (SIGUSR2, &sigact_usr2, NULL); + PR_ASSERT(0 == rv); + + sigemptyset (&sigwait_set); +#if defined(PT_NO_SIGTIMEDWAIT) + sigaddset (&sigwait_set, SIGUSR1); +#else + sigaddset (&sigwait_set, SIGUSR2); +#endif /* defined(PT_NO_SIGTIMEDWAIT) */ + } +#if defined(PT_NO_SIGTIMEDWAIT) + { + struct sigaction sigact_null = {0}; + sigact_null.sa_handler = null_signal_handler; + sigact_null.sa_flags = SA_RESTART; + sigemptyset (&sigact_null.sa_mask); + rv = sigaction (SIGUSR1, &sigact_null, NULL); + PR_ASSERT(0 ==rv); + } +#endif /* defined(PT_NO_SIGTIMEDWAIT) */ +#endif /* defined(_PR_DCETHREADS) */ +} + +PR_IMPLEMENT(void) PR_SetThreadGCAble() +{ + PR_Lock(pt_book.ml); + PR_CurrentThread()->state |= PT_THREAD_GCABLE; + PR_Unlock(pt_book.ml); +} + +PR_IMPLEMENT(void) PR_ClearThreadGCAble() +{ + PR_Lock(pt_book.ml); + PR_CurrentThread()->state &= (~PT_THREAD_GCABLE); + PR_Unlock(pt_book.ml); +} + +#if defined(DEBUG) +static PRBool suspendAllOn = PR_FALSE; +#endif + +static PRBool suspendAllSuspended = PR_FALSE; + +/* Are all GCAble threads (except gc'ing thread) suspended? */ +PR_IMPLEMENT(PRBool) PR_SuspendAllSuspended() +{ + return suspendAllSuspended; +} /* PR_SuspendAllSuspended */ + +PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) +{ + PRIntn count = 0; + PRStatus rv = PR_SUCCESS; + PRThread* thred = pt_book.first; + PRThread *me = PR_CurrentThread(); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_EnumerateThreads\n")); + /* + * $$$ + * Need to suspend all threads other than me before doing this. + * This is really a gross and disgusting thing to do. The only + * good thing is that since all other threads are suspended, holding + * the lock during a callback seems like child's play. + * $$$ + */ + PR_ASSERT(suspendAllOn); + + while (thred != NULL) + { + /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking + * qp->next after applying the function "func". In particular, "func" + * might remove the thread from the queue and put it into another one in + * which case qp->next no longer points to the next entry in the original + * queue. + * + * To get around this problem, we save qp->next in qp_next before applying + * "func" and use that saved value as the next value after applying "func". + */ + PRThread* next = thred->next; + + if (thred->state & PT_THREAD_GCABLE) + { +#if !defined(_PR_DCETHREADS) + PR_ASSERT((thred == me) || (thred->suspend & PT_THREAD_SUSPENDED)); +#endif + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("In PR_EnumerateThreads callback thread %X thid = %X\n", + thred, thred->id)); + + rv = func(thred, count++, arg); + if (rv != PR_SUCCESS) + return rv; + } + thred = next; + } + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End PR_EnumerateThreads count = %d \n", count)); + return rv; +} /* PR_EnumerateThreads */ + +/* + * PR_SuspendAll and PR_ResumeAll are called during garbage collection. The strategy + * we use is to send a SIGUSR2 signal to every gc able thread that we intend to suspend. + * The signal handler will record the stack pointer and will block until resumed by + * the resume call. Since the signal handler is the last routine called for the + * suspended thread, the stack pointer will also serve as a place where all the + * registers have been saved on the stack for the previously executing routines. + * + * Through global variables, we also make sure that PR_Suspend and PR_Resume does not + * proceed until the thread is suspended or resumed. + */ + +#if !defined(_PR_DCETHREADS) + +/* + * In the signal handler, we can not use condition variable notify or wait. + * This does not work consistently across all pthread platforms. We also can not + * use locking since that does not seem to work reliably across platforms. + * Only thing we can do is yielding while testing for a global condition + * to change. This does work on pthread supported platforms. We may have + * to play with priortities if there are any problems detected. + */ + + /* + * In AIX, you cannot use ANY pthread calls in the signal handler except perhaps + * pthread_yield. But that is horribly inefficient. Hence we use only sigwait, no + * sigtimedwait is available. We need to use another user signal, SIGUSR1. Actually + * SIGUSR1 is also used by exec in Java. So our usage here breaks the exec in Java, + * for AIX. You cannot use pthread_cond_wait or pthread_delay_np in the signal + * handler as all synchronization mechanisms just break down. + */ + +#if defined(PT_NO_SIGTIMEDWAIT) +static void null_signal_handler(PRIntn sig) +{ + return; +} +#endif + +static void suspend_signal_handler(PRIntn sig) +{ + PRThread *me = PR_CurrentThread(); + + PR_ASSERT(me != NULL); + PR_ASSERT(me->state & PT_THREAD_GCABLE); + PR_ASSERT((me->suspend & PT_THREAD_SUSPENDED) == 0); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin suspend_signal_handler thred %X thread id = %X\n", + me, me->id)); + + /* + * save stack pointer + */ + me->sp = &me; + + /* + At this point, the thread's stack pointer has been saved, + And it is going to enter a wait loop until it is resumed. + So it is _really_ suspended + */ + + me->suspend |= PT_THREAD_SUSPENDED; + + /* + * now, block current thread + */ +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_cond_signal(&me->suspendResumeCV); + while (me->suspend & PT_THREAD_SUSPENDED) + { +#if !defined(FREEBSD) /*XXX*/ + PRIntn rv; + sigwait(&sigwait_set, &rv); +#endif + } + me->suspend |= PT_THREAD_RESUMED; + pthread_cond_signal(&me->suspendResumeCV); +#else /* defined(PT_NO_SIGTIMEDWAIT) */ + while (me->suspend & PT_THREAD_SUSPENDED) + { + PRIntn rv = sigtimedwait(&sigwait_set, NULL, &hundredmillisec); + PR_ASSERT(-1 == rv); + } + me->suspend |= PT_THREAD_RESUMED; +#endif + + /* + * At this point, thread has been resumed, so set a global condition. + * The ResumeAll needs to know that this has really been resumed. + * So the signal handler sets a flag which PR_ResumeAll will reset. + * The PR_ResumeAll must reset this flag ... + */ + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End suspend_signal_handler thred = %X tid = %X\n", me, me->id)); +} /* suspend_signal_handler */ + +static void PR_SuspendSet(PRThread *thred) +{ + PRIntn rv; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("PR_SuspendSet thred %X thread id = %X\n", thred, thred->id)); + + + /* + * Check the thread state and signal the thread to suspend + */ + + PR_ASSERT((thred->suspend & PT_THREAD_SUSPENDED) == 0); + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("doing pthread_kill in PR_SuspendSet thred %X tid = %X\n", + thred, thred->id)); + rv = pthread_kill (thred->id, SIGUSR2); + PR_ASSERT(0 == rv); +} + +static void PR_SuspendTest(PRThread *thred) +{ + PRIntn rv; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin PR_SuspendTest thred %X thread id = %X\n", thred, thred->id)); + + + /* + * Wait for the thread to be really suspended. This happens when the + * suspend signal handler stores the stack pointer and sets the state + * to suspended. + */ + +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_mutex_lock(&thred->suspendResumeMutex); + while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) + { + pthread_cond_timedwait( + &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); + } + pthread_mutex_unlock(&thred->suspendResumeMutex); +#else + while ((thred->suspend & PT_THREAD_SUSPENDED) == 0) + { + rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); + PR_ASSERT(-1 == rv); + } +#endif + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End PR_SuspendTest thred %X tid %X\n", thred, thred->id)); +} /* PR_SuspendTest */ + +PR_IMPLEMENT(void) PR_ResumeSet(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("PR_ResumeSet thred %X thread id = %X\n", thred, thred->id)); + + /* + * Clear the global state and set the thread state so that it will + * continue past yield loop in the suspend signal handler + */ + + PR_ASSERT(thred->suspend & PT_THREAD_SUSPENDED); + + + thred->suspend &= ~PT_THREAD_SUSPENDED; + +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_kill(thred->id, SIGUSR1); +#endif + +} /* PR_ResumeSet */ + +PR_IMPLEMENT(void) PR_ResumeTest(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("Begin PR_ResumeTest thred %X thread id = %X\n", thred, thred->id)); + + /* + * Wait for the threads resume state to change + * to indicate it is really resumed + */ +#if defined(PT_NO_SIGTIMEDWAIT) + pthread_mutex_lock(&thred->suspendResumeMutex); + while ((thred->suspend & PT_THREAD_RESUMED) == 0) + { + pthread_cond_timedwait( + &thred->suspendResumeCV, &thred->suspendResumeMutex, &onemillisec); + } + pthread_mutex_unlock(&thred->suspendResumeMutex); +#else + while ((thred->suspend & PT_THREAD_RESUMED) == 0) { + PRIntn rv = sigtimedwait(&sigwait_set, NULL, &onemillisec); + PR_ASSERT(-1 == rv); + } +#endif + + thred->suspend &= ~PT_THREAD_RESUMED; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ( + "End PR_ResumeTest thred %X tid %X\n", thred, thred->id)); +} /* PR_ResumeTest */ + +PR_IMPLEMENT(void) PR_SuspendAll() +{ +#ifdef DEBUG + PRIntervalTime stime, etime; +#endif + PRThread* thred = pt_book.first; + PRThread *me = PR_CurrentThread(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); + /* + * Stop all threads which are marked GC able. + */ + PR_Lock(pt_book.ml); +#ifdef DEBUG + suspendAllOn = PR_TRUE; + stime = PR_IntervalNow(); +#endif + while (thred != NULL) + { + if ((thred != me) && (thred->state & PT_THREAD_GCABLE)) + PR_SuspendSet(thred); + thred = thred->next; + } + + /* Wait till they are really suspended */ + thred = pt_book.first; + while (thred != NULL) + { + if ((thred != me) && (thred->state & PT_THREAD_GCABLE)) + PR_SuspendTest(thred); + thred = thred->next; + } + + suspendAllSuspended = PR_TRUE; + +#ifdef DEBUG + etime = PR_IntervalNow(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS,\ + ("End PR_SuspendAll (time %dms)\n", + PR_IntervalToMilliseconds(etime - stime))); +#endif +} /* PR_SuspendAll */ + +PR_IMPLEMENT(void) PR_ResumeAll() +{ +#ifdef DEBUG + PRIntervalTime stime, etime; +#endif + PRThread* thred = pt_book.first; + PRThread *me = PR_CurrentThread(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); + /* + * Resume all previously suspended GC able threads. + */ + suspendAllSuspended = PR_FALSE; +#ifdef DEBUG + stime = PR_IntervalNow(); +#endif + + while (thred != NULL) + { + if ((thred != me) && (thred->state & PT_THREAD_GCABLE)) + PR_ResumeSet(thred); + thred = thred->next; + } + + thred = pt_book.first; + while (thred != NULL) + { + if ((thred != me) && (thred->state & PT_THREAD_GCABLE)) + PR_ResumeTest(thred); + thred = thred->next; + } + + PR_Unlock(pt_book.ml); +#ifdef DEBUG + suspendAllOn = PR_FALSE; + etime = PR_IntervalNow(); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("End PR_ResumeAll (time %dms)\n", + PR_IntervalToMilliseconds(etime - stime))); +#endif +} /* PR_ResumeAll */ + +/* Return the stack pointer for the given thread- used by the GC */ +PR_IMPLEMENT(void *)PR_GetSP(PRThread *thred) +{ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, + ("in PR_GetSP thred %X thid = %X, sp = %X \n", + thred, thred->id, thred->sp)); + return thred->sp; +} /* PR_GetSP */ + +#else /* !defined(_PR_DCETHREADS) */ + +/* + * For DCE threads, there is no pthread_kill or a way of suspending or resuming a + * particular thread. We will just disable the preemption (virtual timer alarm) and + * let the executing thread finish the garbage collection. This stops all other threads + * (GC able or not) and is very inefficient but there is no other choice. + */ +PR_IMPLEMENT(void) PR_SuspendAll() +{ + PRIntn rv; + + suspendAllOn = PR_TRUE; + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_SuspendAll\n")); + /* + * turn off preemption - i.e add virtual alarm signal to the set of + * blocking signals + */ + rv = sigprocmask( + SIG_BLOCK, &javagc_vtalarm_sigmask, &javagc_intsoff_sigmask); + PR_ASSERT(0 == rv); + suspendAllSuspended = PR_TRUE; + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_SuspendAll\n")); +} /* PR_SuspendAll */ + +PR_IMPLEMENT(void) PR_ResumeAll() +{ + PRIntn rv; + + suspendAllSuspended = PR_FALSE; + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_ResumeAll\n")); + /* turn on preemption - i.e re-enable virtual alarm signal */ + + rv = sigprocmask(SIG_SETMASK, &javagc_intsoff_sigmask, (sigset_t *)NULL); + PR_ASSERT(0 == rv); + suspendAllOn = PR_FALSE; + + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_ResumeAll\n")); +} /* PR_ResumeAll */ + +/* Return the stack pointer for the given thread- used by the GC */ +PR_IMPLEMENT(void*)PR_GetSP(PRThread *thred) +{ + pthread_t tid = thred->id; + char *thread_tcb, *top_sp; + + /* + * For HPUX DCE threads, pthread_t is a struct with the + * following three fields (see pthread.h, dce/cma.h): + * cma_t_address field1; + * short int field2; + * short int field3; + * where cma_t_address is typedef'd to be either void* + * or char*. + */ + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("Begin PR_GetSP\n")); + thread_tcb = (char*)tid.field1; + top_sp = *(char**)(thread_tcb + 128); + PR_LOG(_pr_gc_lm, PR_LOG_ALWAYS, ("End PR_GetSP %X \n", top_sp)); + return top_sp; +} /* PR_GetSP */ + +#endif /* !defined(_PR_DCETHREADS) */ + +#endif /* defined(_PR_PTHREADS) || defined(_PR_DCETHREADS) */ + +/* ptthread.c */ diff --git a/pr/src/resource.h b/pr/src/resource.h new file mode 100644 index 00000000..8350f5a9 --- /dev/null +++ b/pr/src/resource.h @@ -0,0 +1,33 @@ +/* -*- 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. + */ + +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by nspr.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/pr/src/threads/Makefile b/pr/src/threads/Makefile new file mode 100644 index 00000000..91ad0aaa --- /dev/null +++ b/pr/src/threads/Makefile @@ -0,0 +1,59 @@ +# +# 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 + +ifdef USE_PTHREADS + DIRS = +else + DIRS = combined +endif + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +ifdef USE_PTHREADS +CSRCS = prcmon.c \ + $(NULL) +else +CSRCS = \ + prcmon.c \ + prdump.c \ + prmon.c \ + prsem.c \ + prcthr.c \ + prtpd.c \ + $(NULL) +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/threads/combined/Makefile b/pr/src/threads/combined/Makefile new file mode 100644 index 00000000..676d8817 --- /dev/null +++ b/pr/src/threads/combined/Makefile @@ -0,0 +1,52 @@ +# +# 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 + +# Disable optimization of the nspr on SunOS4.1.3 +ifeq ($(OS_ARCH),SunOS) +ifeq ($(OS_RELEASE),4.1.3_U1) +OPTIMIZER = +endif +endif + +ifdef USE_PTHREADS +CSRCS = \ + $(NULL) +else +CSRCS = \ + prucpu.c \ + prucv.c \ + prulock.c \ + pruthr.c \ + prustack.c \ + $(NULL) +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/threads/combined/README b/pr/src/threads/combined/README new file mode 100644 index 00000000..aa266652 --- /dev/null +++ b/pr/src/threads/combined/README @@ -0,0 +1,62 @@ +NSPR 2.0 evolution +------------------ + + +Phase 1- today + +Currently (Oct 10, 1996) NSPR 2.0 has two modes. Either _PR_NTHREAD +is defined, in which case the PR_CreateThread() call always creates a +native kernel thread, or _PR_NTHREAD is not defined and PR_CreateThread() +always creates user level threads within the single, original process. This +source code is reflected in two directories, nspr20/pr/src/threads/native, and +nspr20/pr/src/threads/user. Although the PR_CreateThread() function has +a paramter to specify the "scope" of a thread, this parameter is not yet +used- except on solaris where it uses it to specify bound vs unbound threads. + +Phase 2 - next week + +The next step is to provide a combination of user and native threads. The +idea, of course, is to have some small number of native threads and each of +those threads be able to run user level threads. The number of native +threads created will most likely be proportional to the number of CPUs in +the system. For this reason, the specific set of native threads which are +used to run the user-level threads will be called "CPU" threads. + +The user level threads which will be run on the CPU threads are able to +run on any of the CPU threads available, and over the course of a user-level +thread's lifetime, it may drift from one CPU thread to another. All +user-level threads will compete for processing time via a single run queue. + +Creation of a CPU thread will be primarily controlled by NSPR itself or by +the user running a function PR_Concurrency(). The details of PR_Concurrency() +have not yet been worked out; but the idea is that the user can specify to +NSPR how many CPU threads are desired. + +In this system, user-level threads are created by using PR_CreateThread() and +specifying the PR_LOCAL_SCOPE option. LOCAL_SCOPE indicates that the thread +will be under the control of the "local" scheduler. Creating threads with +GLOBAL_SCOPE, on the other hand will create a thread which is under the +control of the system's scheduler. In otherwords, this creates a native thread +which is not a CPU thread; it runs a single thread task and never has more +than one task to run. LOCAL_SCOPE is much like creating a Solaris unbound +thread, while GLOBAL_SCOPE is similar to creating a Solaris bound thread. + +To implement this architecture, the source code will still maintain the "user" +and "native" directories which is has today. However a third directory +"combined" will also exist. To compile a version of NSPR which only creates +native threads, the user can define _PR_NTHREAD. For exclusive user-level +threads, do not define _PR_NTHREAD. To get the combined threads, define +_PR_NTHREAD and _PR_USE_CPUS. + + +Phase 3 - later than next week + +The goal is to eliminate the 3 directories. Once these three models are in +place, the remaining work will be to eliminate the native and user thread +directories for all platforms, so that the entire thread model is contained +within what is today called the "combined" model. This new and glorious +source code will attempt to make the "combined" model on any platforms which +provide the necessary underlying native threading, but will also be +capable of using exclusive user-level threads on systems which don't have +native threads. + diff --git a/pr/src/threads/combined/prucpu.c b/pr/src/threads/combined/prucpu.c new file mode 100644 index 00000000..b0e4f5fd --- /dev/null +++ b/pr/src/threads/combined/prucpu.c @@ -0,0 +1,304 @@ +/* -*- 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" + +_PRCPU *_pr_primordialCPU; + +PRInt32 _pr_md_idle_cpus; /* number of idle cpus */ +/* + * The idle threads in MxN models increment/decrement _pr_md_idle_cpus. + * If _PR_HAVE_ATOMIC_OPS is not defined, they can't use the atomic + * increment/decrement routines (which are based on PR_Lock/PR_Unlock), + * because PR_Lock asserts that the calling thread is not an idle thread. + * So we use a _MDLock to protect _pr_md_idle_cpus. + */ +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifndef _PR_HAVE_ATOMIC_OPS +static _MDLock _pr_md_idle_cpus_lock; +#endif +#endif +PRUintn _pr_numCPU; +PRInt32 _pr_cpus_exit; +PRInt32 _pr_cpu_affinity_mask = 0; + +#if !defined (_PR_GLOBAL_THREADS_ONLY) + +static PRUintn _pr_cpuID; + +static void PR_CALLBACK _PR_CPU_Idle(void *); +static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue); + +void _PR_InitCPUs() +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + _pr_cpuID = 0; + _MD_NEW_LOCK( &_pr_cpuLock); +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifndef _PR_HAVE_ATOMIC_OPS + _MD_NEW_LOCK(&_pr_md_idle_cpus_lock); +#endif +#endif + +#ifdef HAVE_CUSTOM_USER_THREADS + _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); +#endif + + /* Now start the first CPU. */ + _pr_primordialCPU = _PR_CreateCPU(me, PR_TRUE); + _pr_numCPU = 1; + + _PR_MD_SET_CURRENT_CPU(_pr_primordialCPU); + + /* Initialize cpu for current thread (could be different from me) */ + _PR_MD_CURRENT_THREAD()->cpu = _pr_primordialCPU; + + _PR_MD_SET_LAST_THREAD(me); + + _PR_MD_INIT_CPUS(); +} + + +static _PRCPUQueue *_PR_CreateCPUQueue(void) +{ + PRInt32 index; + _PRCPUQueue *cpuQueue; + cpuQueue = PR_NEWZAP(_PRCPUQueue); + + _MD_NEW_LOCK( &cpuQueue->runQLock ); + _MD_NEW_LOCK( &cpuQueue->sleepQLock ); + _MD_NEW_LOCK( &cpuQueue->miscQLock ); + + for (index = 0; index < PR_PRIORITY_LAST + 1; index++) + PR_INIT_CLIST( &(cpuQueue->runQ[index]) ); + PR_INIT_CLIST( &(cpuQueue->sleepQ) ); + PR_INIT_CLIST( &(cpuQueue->pauseQ) ); + PR_INIT_CLIST( &(cpuQueue->suspendQ) ); + PR_INIT_CLIST( &(cpuQueue->waitingToJoinQ) ); + + cpuQueue->numCPUs = 1; + + return cpuQueue; +} + +/* + * Create a new CPU. + */ +static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue) +{ + _PRCPU *cpu; + + /* + ** Create a new cpu. The assumption this code makes is that the + ** underlying operating system creates a stack to go with the new + ** native thread. That stack will be used by the cpu when pausing. + */ + cpu = PR_NEWZAP(_PRCPU); + if (cpu) { + + cpu->last_clock = PR_IntervalNow(); + + if (needQueue == PR_TRUE) + cpu->queue = _PR_CreateCPUQueue(); + else + cpu->queue = _PR_MD_CURRENT_CPU()->queue; + + if (!cpu->queue) { + PR_DELETE(cpu); + return NULL; + } + + /* Before we create any threads on this CPU we have to + * set the current CPU + */ + _PR_MD_SET_CURRENT_CPU(cpu); + _PR_MD_INIT_RUNNING_CPU(cpu); + thread->cpu = cpu; + + cpu->idle_thread = _PR_CreateThread(PR_SYSTEM_THREAD, + _PR_CPU_Idle, + (void *)cpu, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, + _PR_IDLE_THREAD); + + if (!cpu->idle_thread) { + /* didn't clean up CPU queue XXXMB */ + PR_DELETE(cpu); + return NULL; + } + cpu->idle_thread->no_sched = 0; + + cpu->thread = thread; + + if (_pr_cpu_affinity_mask) + PR_SetThreadAffinityMask(thread, _pr_cpu_affinity_mask); + + /* Created a new CPU */ + _PR_CPU_LIST_LOCK(); + cpu->id = _pr_cpuID++; + PR_APPEND_LINK(&cpu->links, &_PR_CPUQ()); + _PR_CPU_LIST_UNLOCK(); + } + return cpu; +} + +/* +** This code is used during a cpu's initial creation. +*/ +static void _PR_RunCPU(void *unused) +{ +#if defined(XP_MAC) +#pragma unused (unused) +#endif + + _PRCPU *cpu; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(NULL != me); + +#ifdef HAVE_CUSTOM_USER_THREADS + _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); +#endif + + me->no_sched = 1; + cpu = _PR_CreateCPU(me, PR_TRUE); + + _PR_MD_SET_CURRENT_CPU(cpu); + _PR_MD_SET_CURRENT_THREAD(cpu->thread); + me->cpu = cpu; + while(1) { + PRInt32 is; + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); + _PR_MD_START_INTERRUPTS(); + _PR_MD_SWITCH_CONTEXT(me); + } +} + +static void PR_CALLBACK _PR_CPU_Idle(void *_cpu) +{ + _PRCPU *cpu = (_PRCPU *)_cpu; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(NULL != me); + + me->cpu = cpu; + cpu->idle_thread = me; + if (_MD_LAST_THREAD()) + _MD_LAST_THREAD()->no_sched = 0; + if (!_PR_IS_NATIVE_THREAD(me)) _PR_SET_INTSOFF(0); + while(1) { + PRInt32 is; + PRIntervalTime timeout; + + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); + + _PR_RUNQ_LOCK(cpu); +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifdef _PR_HAVE_ATOMIC_OPS + _PR_MD_ATOMIC_INCREMENT(&_pr_md_idle_cpus); +#else + _PR_MD_LOCK(&_pr_md_idle_cpus_lock); + _pr_md_idle_cpus++; + _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock); +#endif /* _PR_HAVE_ATOMIC_OPS */ +#endif + /* If someone on runq; do a nonblocking PAUSECPU */ + if (_PR_RUNQREADYMASK(me->cpu) != 0) { + _PR_RUNQ_UNLOCK(cpu); + timeout = PR_INTERVAL_NO_WAIT; + } else { + _PR_RUNQ_UNLOCK(cpu); + + _PR_SLEEPQ_LOCK(cpu); + if (PR_CLIST_IS_EMPTY(&_PR_SLEEPQ(me->cpu))) { + timeout = PR_INTERVAL_NO_TIMEOUT; + } else { + PRThread *wakeThread; + wakeThread = _PR_THREAD_PTR(_PR_SLEEPQ(me->cpu).next); + timeout = wakeThread->sleep; + } + _PR_SLEEPQ_UNLOCK(cpu); + } + + + /* Wait for an IO to complete */ + (void)_PR_MD_PAUSE_CPU(timeout); + +#if !defined(_PR_LOCAL_THREADS_ONLY) && !defined(_PR_GLOBAL_THREADS_ONLY) +#ifdef _PR_HAVE_ATOMIC_OPS + _PR_MD_ATOMIC_DECREMENT(&_pr_md_idle_cpus); +#else + _PR_MD_LOCK(&_pr_md_idle_cpus_lock); + _pr_md_idle_cpus--; + _PR_MD_UNLOCK(&_pr_md_idle_cpus_lock); +#endif /* _PR_HAVE_ATOMIC_OPS */ +#endif + + _PR_ClockInterrupt(); + + /* Now schedule any thread that is on the runq + * INTS must be OFF when calling PR_Schedule() + */ + me->state = _PR_RUNNABLE; + _PR_MD_SWITCH_CONTEXT(me); + if (!_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); + } +} +#endif /* _PR_GLOBAL_THREADS_ONLY */ + + +PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) +{ +#if !defined(_PR_GLOBAL_THREADS_ONLY) && !defined(_PR_LOCAL_THREADS_ONLY) + + PRUintn newCPU; + PRThread *cpu; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + _PR_CPU_LIST_LOCK(); + if (_pr_numCPU < numCPUs) { + newCPU = numCPUs - _pr_numCPU; + _pr_numCPU = numCPUs; + } else newCPU = 0; + _PR_CPU_LIST_UNLOCK(); + + for (; newCPU; newCPU--) { + cpu = _PR_CreateThread(PR_SYSTEM_THREAD, + _PR_RunCPU, + NULL, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0, + _PR_IDLE_THREAD); + } +#endif +} + +PR_IMPLEMENT(_PRCPU *) _PR_GetPrimordialCPU(void) +{ + if (_pr_primordialCPU) + return _pr_primordialCPU; + else + return _PR_MD_CURRENT_CPU(); +} diff --git a/pr/src/threads/combined/prucv.c b/pr/src/threads/combined/prucv.c new file mode 100644 index 00000000..08962d9f --- /dev/null +++ b/pr/src/threads/combined/prucv.c @@ -0,0 +1,634 @@ +/* -*- 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 "prinrval.h" +#include "prtypes.h" + + +/* +** Notify one thread that it has finished waiting on a condition variable +** Caller must hold the _PR_CVAR_LOCK(cv) +*/ +PRBool NotifyThread (PRThread *thread, PRThread *me) +{ + PRBool rv; + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + _PR_THREAD_LOCK(thread); + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + if (thread->wait.cvar != NULL) { + thread->wait.cvar = NULL; + + _PR_SLEEPQ_LOCK(thread->cpu); + /* The notify and timeout can collide; in which case both may + * attempt to delete from the sleepQ; only let one do it. + */ + if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) + _PR_DEL_SLEEPQ(thread, PR_TRUE); + _PR_SLEEPQ_UNLOCK(thread->cpu); + + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will move it to the runQ + */ + thread->state = _PR_SUSPENDED; + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_SUSPENDQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + _PR_THREAD_UNLOCK(thread); + } else { + /* Make thread runnable */ + thread->state = _PR_RUNNABLE; + _PR_THREAD_UNLOCK(thread); + + _PR_AddThreadToRunQ(me, thread); + _PR_MD_WAKEUP_WAITER(thread); + } + + rv = PR_TRUE; + } else { + /* Thread has already been notified */ + _PR_THREAD_UNLOCK(thread); + rv = PR_FALSE; + } + } else { /* If the thread is a native thread */ + if (thread->wait.cvar) { + thread->wait.cvar = NULL; + + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will enable the thread to run + */ + thread->state = _PR_SUSPENDED; + } else + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + _PR_MD_WAKEUP_WAITER(thread); + rv = PR_TRUE; + } else { + _PR_THREAD_UNLOCK(thread); + rv = PR_FALSE; + } + } + + return rv; +} + +/* + * Notify thread waiting on cvar; called when thread is interrupted + * The thread lock is held on entry and released before return + */ +void _PR_NotifyLockedThread (PRThread *thread) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCondVar *cvar; + PRThreadPriority pri; + + if ( !_PR_IS_NATIVE_THREAD(me)) + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + + cvar = thread->wait.cvar; + thread->wait.cvar = NULL; + _PR_THREAD_UNLOCK(thread); + + _PR_CVAR_LOCK(cvar); + _PR_THREAD_LOCK(thread); + + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_SLEEPQ_LOCK(thread->cpu); + /* The notify and timeout can collide; in which case both may + * attempt to delete from the sleepQ; only let one do it. + */ + if (thread->flags & (_PR_ON_SLEEPQ|_PR_ON_PAUSEQ)) + _PR_DEL_SLEEPQ(thread, PR_TRUE); + _PR_SLEEPQ_UNLOCK(thread->cpu); + + /* Make thread runnable */ + pri = thread->priority; + thread->state = _PR_RUNNABLE; + + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + + _PR_AddThreadToRunQ(me, thread); + _PR_THREAD_UNLOCK(thread); + + _PR_MD_WAKEUP_WAITER(thread); + } else { + if (thread->flags & _PR_SUSPENDING) { + /* + * set thread state to SUSPENDED; a Resume operation + * on the thread will enable the thread to run + */ + thread->state = _PR_SUSPENDED; + } else + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + _PR_MD_WAKEUP_WAITER(thread); + } + + _PR_CVAR_UNLOCK(cvar); + return; +} + +/* +** Make the given thread wait for the given condition variable +*/ +PRStatus _PR_WaitCondVar( + PRThread *thread, PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) +{ + intn is; + PRStatus rv = PR_SUCCESS; + + PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + +#ifdef _PR_GLOBAL_THREADS_ONLY + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + thread->wait.cvar = cvar; + lock->owner = NULL; + _PR_MD_WAIT_CV(&cvar->md,&lock->ilock, timeout); + thread->wait.cvar = NULL; + lock->owner = thread; + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if ( !_PR_IS_NATIVE_THREAD(thread)) + _PR_INTSOFF(is); + + _PR_CVAR_LOCK(cvar); + _PR_THREAD_LOCK(thread); + + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + _PR_CVAR_UNLOCK(cvar); + _PR_THREAD_UNLOCK(thread); + if ( !_PR_IS_NATIVE_THREAD(thread)) + _PR_INTSON(is); + return PR_FAILURE; + } + + thread->state = _PR_COND_WAIT; + thread->wait.cvar = cvar; + + /* + ** Put the caller thread on the condition variable's wait Q + */ + PR_APPEND_LINK(&thread->waitQLinks, &cvar->condQ); + + /* Note- for global scope threads, we don't put them on the + * global sleepQ, so each global thread must put itself + * to sleep only for the time it wants to. + */ + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_SLEEPQ_LOCK(thread->cpu); + _PR_ADD_SLEEPQ(thread, timeout); + _PR_SLEEPQ_UNLOCK(thread->cpu); + } + _PR_CVAR_UNLOCK(cvar); + _PR_THREAD_UNLOCK(thread); + + /* + ** Release lock protecting the condition variable and thereby giving time + ** to the next thread which can potentially notify on the condition variable + */ + PR_Unlock(lock); + + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, + ("PR_Wait: cvar=%p waiting for %d", cvar, timeout)); + + rv = _PR_MD_WAIT(thread, timeout); + + _PR_CVAR_LOCK(cvar); + PR_REMOVE_LINK(&thread->waitQLinks); + _PR_CVAR_UNLOCK(cvar); + + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, + ("PR_Wait: cvar=%p done waiting", cvar)); + + if ( !_PR_IS_NATIVE_THREAD(thread)) + _PR_INTSON(is); + + /* Acquire lock again that we had just relinquished */ + PR_Lock(lock); + + if (_PR_PENDING_INTERRUPT(thread)) { + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + thread->flags &= ~_PR_INTERRUPT; + return PR_FAILURE; + } + + return rv; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +void _PR_NotifyCondVar(PRCondVar *cvar, PRThread *me) +{ +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_NOTIFY_CV(&cvar->md, &cvar->lock->ilock); +#else /* _PR_GLOBAL_THREADS_ONLY */ + + PRCList *q; + PRIntn is; + + if ( !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + _PR_CVAR_LOCK(cvar); + q = cvar->condQ.next; + while (q != &cvar->condQ) { +#ifndef XP_MAC + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("_PR_NotifyCondVar: cvar=%p", cvar)); +#endif + if (_PR_THREAD_CONDQ_PTR(q)->wait.cvar) { + if (NotifyThread(_PR_THREAD_CONDQ_PTR(q), me) == PR_TRUE) + break; + } + q = q->next; + } + _PR_CVAR_UNLOCK(cvar); + + if ( !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Cndition variable debugging log info. +*/ +PRUint32 _PR_CondVarToString(PRCondVar *cvar, char *buf, PRUint32 buflen) +{ + PRUint32 nb; + + if (cvar->lock->owner) { + nb = PR_snprintf(buf, buflen, "[%p] owner=%ld[%p]", + cvar, cvar->lock->owner->id, cvar->lock->owner); + } else { + nb = PR_snprintf(buf, buflen, "[%p]", cvar); + } + return nb; +} + +/* +** Expire condition variable waits that are ready to expire. "now" is the current +** time. +*/ +void _PR_ClockInterrupt(void) +{ + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); + _PRCPU *cpu = me->cpu; + PRIntervalTime elapsed, now; + + PR_ASSERT(_PR_MD_GET_INTSOFF() != 0); + /* Figure out how much time elapsed since the last clock tick */ + now = PR_IntervalNow(); + elapsed = now - cpu->last_clock; + cpu->last_clock = now; + +#ifndef XP_MAC + PR_LOG(_pr_clock_lm, PR_LOG_MAX, + ("ExpireWaits: elapsed=%lld usec", elapsed)); +#endif + + while(1) { + _PR_SLEEPQ_LOCK(cpu); + if (_PR_SLEEPQ(cpu).next == &_PR_SLEEPQ(cpu)) { + _PR_SLEEPQ_UNLOCK(cpu); + break; + } + + thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next); + + if (elapsed < thread->sleep) { + thread->sleep -= elapsed; + _PR_SLEEPQMAX(thread->cpu) -= elapsed; + PR_ASSERT((PRInt32)(thread->sleep) >= 0); + _PR_SLEEPQ_UNLOCK(cpu); + break; + } + _PR_SLEEPQ_UNLOCK(cpu); + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); + + _PR_THREAD_LOCK(thread); + + /* + ** Consume this sleeper's amount of elapsed time from the elapsed + ** time value. The next remaining piece of elapsed time will be + ** available for the next sleeping thread's timer. + */ + _PR_SLEEPQ_LOCK(cpu); + PR_ASSERT(!(thread->flags & _PR_ON_PAUSEQ)); + if (thread->flags & _PR_ON_SLEEPQ) { + _PR_DEL_SLEEPQ(thread, PR_FALSE); + elapsed -= thread->sleep; + _PR_SLEEPQ_UNLOCK(cpu); + } else { + /* Thread was already handled; Go get another one */ + _PR_SLEEPQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(thread); + continue; + } + + /* Notify the thread waiting on the condition variable */ + if (thread->flags & _PR_SUSPENDING) { + PR_ASSERT((thread->state == _PR_IO_WAIT) || + (thread->state == _PR_COND_WAIT)); + /* + ** Thread is suspended and its condition timeout + ** expired. Transfer thread from sleepQ to suspendQ. + */ + thread->wait.cvar = NULL; + _PR_MISCQ_LOCK(cpu); + thread->state = _PR_SUSPENDED; + _PR_ADD_SUSPENDQ(thread, cpu); + _PR_MISCQ_UNLOCK(cpu); + } else { + if (thread->wait.cvar) { + PRThreadPriority pri; + + /* Do work very similar to what NotifyThread does */ + PR_ASSERT( !_PR_IS_NATIVE_THREAD(thread) ); + + /* Make thread runnable */ + pri = thread->priority; + thread->state = _PR_RUNNABLE; + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + + PR_ASSERT(thread->cpu == cpu); + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + + if (pri > me->priority) + _PR_SET_RESCHED_FLAG(); + + thread->wait.cvar = NULL; + + _PR_MD_WAKEUP_WAITER(thread); + + } else if (thread->io_pending == PR_TRUE) { + /* Need to put IO sleeper back on runq */ + int pri = thread->priority; + + thread->io_suspended = PR_TRUE; + + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + PR_ASSERT(thread->cpu == cpu); + thread->state = _PR_RUNNABLE; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + } + } + _PR_THREAD_UNLOCK(thread); + } +} + +/************************************************************************/ + +/* +** Create a new condition variable. +** "lock" is the lock to use with the condition variable. +** +** Condition variables are synchronization objects that threads can use +** to wait for some condition to occur. +** +** This may fail if memory is tight or if some operating system resource +** is low. +*/ +PR_IMPLEMENT(PRCondVar*) PR_NewCondVar(PRLock *lock) +{ + PRCondVar *cvar; + + PR_ASSERT(lock != NULL); + + cvar = PR_NEWZAP(PRCondVar); + if (cvar) { +#ifdef _PR_GLOBAL_THREADS_ONLY + if(_PR_MD_NEW_CV(&cvar->md)) { + PR_DELETE(cvar); + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } +#endif + if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE) { + PR_DELETE(cvar); + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } + cvar->lock = lock; + PR_INIT_CLIST(&cvar->condQ); + + } else { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + } + return cvar; +} + +/* +** Destroy a condition variable. There must be no thread +** waiting on the condvar. The caller is responsible for guaranteeing +** that the condvar is no longer in use. +** +*/ +PR_IMPLEMENT(void) PR_DestroyCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar->condQ.next == &cvar->condQ); + +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_FREE_CV(&cvar->md); +#endif + _PR_MD_FREE_LOCK(&(cvar->ilock)); + + PR_DELETE(cvar); +} + +/* +** Wait for a notify on the condition variable. Sleep for "tiemout" amount +** of ticks (if "timeout" is zero then the sleep is indefinite). While +** the thread is waiting it unlocks lock. When the wait has +** finished the thread regains control of the condition variable after +** locking the associated lock. +** +** The thread waiting on the condvar will be resumed when the condvar is +** notified (assuming the thread is the next in line to receive the +** notify) or when the timeout elapses. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable or the thread has been interrupted. +*/ +extern PRThread *suspendAllThread; +PR_IMPLEMENT(PRStatus) PR_WaitCondVar(PRCondVar *cvar, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + PR_ASSERT(me != suspendAllThread); + if (cvar->lock->owner != me) return PR_FAILURE; + + return _PR_WaitCondVar(me, cvar, cvar->lock, timeout); +} + +/* +** Notify the highest priority thread waiting on the condition +** variable. If a thread is waiting on the condition variable (using +** PR_Wait) then it is awakened and begins waiting on the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyCondVar(PRCondVar *cvar) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + PR_ASSERT(me != suspendAllThread); + if (cvar->lock->owner != me) return PR_FAILURE; + + _PR_NotifyCondVar(cvar, me); + return PR_SUCCESS; +} + +/* +** Notify all of the threads waiting on the condition variable. All of +** threads are notified in turn. The highest priority thread will +** probably acquire the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyAllCondVar(PRCondVar *cvar) +{ + PRCList *q; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(cvar->lock->owner == me); + if (cvar->lock->owner != me) return PR_FAILURE; + +#ifdef _PR_GLOBAL_THREADS_ONLY + _PR_MD_NOTIFYALL_CV(&cvar->md, &cvar->lock->ilock); + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + if ( !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _PR_CVAR_LOCK(cvar); + q = cvar->condQ.next; + while (q != &cvar->condQ) { + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); + NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); + q = q->next; + } + _PR_CVAR_UNLOCK(cvar); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + + +/*********************************************************************/ +/*********************************************************************/ +/********************ROUTINES FOR DCE EMULATION***********************/ +/*********************************************************************/ +/*********************************************************************/ +#include "prpdce.h" + +PR_IMPLEMENT(PRCondVar*) PRP_NewNakedCondVar(void) +{ + PRCondVar *cvar = PR_NEWZAP(PRCondVar); + if (NULL != cvar) + { + if (_PR_MD_NEW_LOCK(&(cvar->ilock)) == PR_FAILURE) + { + PR_DELETE(cvar); cvar = NULL; + } + else + { + PR_INIT_CLIST(&cvar->condQ); + cvar->lock = _PR_NAKED_CV_LOCK; + } + + } + return cvar; +} + +PR_IMPLEMENT(void) PRP_DestroyNakedCondVar(PRCondVar *cvar) +{ + PR_ASSERT(cvar->condQ.next == &cvar->condQ); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + _PR_MD_FREE_LOCK(&(cvar->ilock)); + + PR_DELETE(cvar); +} + +PR_IMPLEMENT(PRStatus) PRP_NakedWait( + PRCondVar *cvar, PRLock *lock, PRIntervalTime timeout) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + return _PR_WaitCondVar(me, cvar, lock, timeout); +} /* PRP_NakedWait */ + +PR_IMPLEMENT(PRStatus) PRP_NakedNotify(PRCondVar *cvar) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + _PR_NotifyCondVar(cvar, me); + + return PR_SUCCESS; +} /* PRP_NakedNotify */ + +PR_IMPLEMENT(PRStatus) PRP_NakedBroadcast(PRCondVar *cvar) +{ + PRCList *q; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PR_ASSERT(_PR_NAKED_CV_LOCK == cvar->lock); + + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); + _PR_MD_LOCK( &(cvar->ilock) ); + q = cvar->condQ.next; + while (q != &cvar->condQ) { + PR_LOG(_pr_cvar_lm, PR_LOG_MIN, ("PR_NotifyAll: cvar=%p", cvar)); + NotifyThread(_PR_THREAD_CONDQ_PTR(q), me); + q = q->next; + } + _PR_MD_UNLOCK( &(cvar->ilock) ); + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); + + return PR_SUCCESS; +} /* PRP_NakedBroadcast */ + diff --git a/pr/src/threads/combined/prulock.c b/pr/src/threads/combined/prulock.c new file mode 100644 index 00000000..0ec89fc9 --- /dev/null +++ b/pr/src/threads/combined/prulock.c @@ -0,0 +1,420 @@ +/* -*- 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" + + +void _PR_InitLocks(void) +{ + _PR_MD_INIT_LOCKS(); +} + +/* +** Deal with delayed interrupts/requested reschedule during interrupt +** re-enables. +*/ +void _PR_IntsOn(_PRCPU *cpu) +{ + PRUintn missed, pri, i; + _PRInterruptTable *it; + PRThread *me; + + PR_ASSERT(cpu); /* Global threads don't have CPUs */ + PR_ASSERT(_PR_MD_GET_INTSOFF() > 0); + me = _PR_MD_CURRENT_THREAD(); +#if !defined(XP_MAC) + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); +#endif + + /* + ** Process delayed interrupts. This logic is kinda scary because we + ** need to avoid losing an interrupt (it's ok to delay an interrupt + ** until later). + ** + ** There are two missed state words. _pr_ints.where indicates to the + ** interrupt handler which state word is currently safe for + ** modification. + ** + ** This code scans both interrupt state words, using the where flag + ** to indicate to the interrupt which state word is safe for writing. + ** If an interrupt comes in during a scan the other word will be + ** modified. This modification will be noticed during the next + ** iteration of the loop or during the next call to this routine. + */ + for (i = 0; i < 2; i++) { + cpu->where = (1 - i); + missed = cpu->u.missed[i]; + if (missed != 0) { + cpu->u.missed[i] = 0; + for (it = _pr_interruptTable; it->name; it++) { + if (missed & it->missed_bit) { +#ifndef XP_MAC + PR_LOG(_pr_sched_lm, PR_LOG_MIN, + ("IntsOn[0]: %s intr", it->name)); +#endif + (*it->handler)(); + } + } + } + } + + if (cpu->u.missed[3] != 0) { + _PRCPU *cpu; + + _PR_THREAD_LOCK(me); + me->state = _PR_RUNNABLE; + pri = me->priority; + + cpu = me->cpu; + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + _PR_THREAD_UNLOCK(me); + _PR_MD_SWITCH_CONTEXT(me); + } +} + +/* +** Assign an idle lock to the first runnable waiting thread. Skip over +** threads that are trying to be suspended +** Note: Caller must hold _PR_LOCK_LOCK() +*/ +PRThread * _PR_AssignLock(PRLock *lock) +{ + PRThread *t = NULL; + PRThread *me; + PRCList *q; + PRThreadPriority pri; + + q = lock->waitQ.next; + PR_ASSERT(q != &lock->waitQ); + while (q != &lock->waitQ) { + /* Assign lock to first waiter */ + t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); + + /* + ** We are about to change the thread's state to runnable and for local + ** threads, we are going to assign a cpu to it. So, protect thread's + ** data structure. + */ + _PR_THREAD_LOCK(t); + + if (t->flags & _PR_SUSPENDING) { + q = q->next; + _PR_THREAD_UNLOCK(t); + t = NULL; + continue; + } + + /* Found a thread to give the lock to */ + PR_ASSERT(t->state == _PR_LOCK_WAIT); + PR_ASSERT(t->wait.lock == lock); + pri = t->priority; + t->wait.lock = 0; + PR_REMOVE_LINK(&t->waitQLinks); /* take it off lock's waitQ */ + + /* Lock inherits the thread's priority */ + lock->priority = pri; + lock->boostPriority = PR_PRIORITY_LOW; + lock->owner = t; + + /* Add the granted lock to this owning thread's lock list */ + PR_APPEND_LINK(&lock->links, &t->lockList); + + /* + ** If the new owner of the lock is a native thread, nothing else to do + ** except to wake it up by calling the machine dependent wakeup routine. + ** + ** If the new owner is a local thread, we need to assign it a cpu and + ** put the thread on that cpu's run queue. There are two cases to take care + ** of. If the currently running thread is also a local thread, we just + ** assign our own cpu to that thread and put it on the cpu's run queue. + ** If the the currently running thread is a native thread, we assign the + ** primordial cpu to it (on NT, MD_WAKEUP handles the cpu assignment). + */ + + if ( !_PR_IS_NATIVE_THREAD(t) ) { + + t->state = _PR_RUNNABLE; + + me = _PR_MD_CURRENT_THREAD(); + + _PR_AddThreadToRunQ(me, t); + _PR_THREAD_UNLOCK(t); + } else { + t->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(t); + } + _PR_MD_WAKEUP_WAITER(t); + break; + } + return t; +} + +/************************************************************************/ + + +PR_IMPLEMENT(PRLock*) PR_NewLock(void) +{ + PRLock *lock; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + lock = PR_NEWZAP(PRLock); + if (lock) { + if (_PR_MD_NEW_LOCK(&lock->ilock) == PR_FAILURE) { + PR_DELETE(lock); + return(NULL); + } + PR_INIT_CLIST(&lock->links); + PR_INIT_CLIST(&lock->waitQ); + } + return lock; +} + +/* +** Destroy the given lock "lock". There is no point in making this race +** free because if some other thread has the pointer to this lock all +** bets are off. +*/ +PR_IMPLEMENT(void) PR_DestroyLock(PRLock *lock) +{ + PR_ASSERT(lock->owner == 0); + _PR_MD_FREE_LOCK(&lock->ilock); + PR_DELETE(lock); +} + +extern PRThread *suspendAllThread; +/* +** Lock the lock. +*/ +PR_IMPLEMENT(void) PR_Lock(PRLock *lock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntn is; + PRThread *t; + PRCList *q; + + PR_ASSERT(me != suspendAllThread); +#if !defined(XP_MAC) + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); +#endif +#ifdef _PR_GLOBAL_THREADS_ONLY + PR_ASSERT(lock->owner != me); + _PR_MD_LOCK(&lock->ilock); + lock->owner = me; + return; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + _PR_LOCK_LOCK(lock); + if (lock->owner == 0) { + /* Just got the lock */ + lock->owner = me; + lock->priority = me->priority; + /* Add the granted lock to this owning thread's lock list */ + PR_APPEND_LINK(&lock->links, &me->lockList); + _PR_LOCK_UNLOCK(lock); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); + return; + } + + /* If this thread already owns this lock, then it is a deadlock */ + PR_ASSERT(lock->owner != me); + + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + +#if 0 + if (me->priority > lock->owner->priority) { + /* + ** Give the lock owner a priority boost until we get the + ** lock. Record the priority we boosted it to. + */ + lock->boostPriority = me->priority; + _PR_SetThreadPriority(lock->owner, me->priority); + } +#endif + + /* + Add this thread to the asked for lock's list of waiting threads. We + add this thread thread in the right priority order so when the unlock + occurs, the thread with the higher priority will get the lock. + */ + /* Sort thread into lock's waitQ at appropriate point */ + q = lock->waitQ.next; + + /* Now scan the list for where to insert this entry */ + while (q != &lock->waitQ) { + t = _PR_THREAD_CONDQ_PTR(lock->waitQ.next); + if (me->priority > t->priority) { + /* Found a lower priority thread to insert in front of */ + break; + } + q = q->next; + } + PR_INSERT_BEFORE(&me->waitQLinks, q); + + /* + Now grab the threadLock since we are about to change the state. We have + to do this since a PR_Suspend or PR_SetThreadPriority type call that takes + a PRThread* as an argument could be changing the state of this thread from + a thread running on a different cpu. + */ + + _PR_THREAD_LOCK(me); + me->state = _PR_LOCK_WAIT; + me->wait.lock = lock; + _PR_THREAD_UNLOCK(me); + + _PR_LOCK_UNLOCK(lock); + + _PR_MD_WAIT(me, PR_INTERVAL_NO_TIMEOUT); + + /* When we are here after the context switch, we better own the lock */ + PR_ASSERT(lock->owner == me); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_FAST_INTSON(is); +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Unlock the lock. +*/ +PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) +{ + PRCList *q; + PRThreadPriority pri, boost; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(lock->owner == me); + PR_ASSERT(me != suspendAllThread); +#if !defined(XP_MAC) + PR_ASSERT(!(me->flags & _PR_IDLE_THREAD)); +#endif + if (lock->owner != me) { + return PR_FAILURE; + } + +#ifdef _PR_GLOBAL_THREADS_ONLY + lock->owner = 0; + _PR_MD_UNLOCK(&lock->ilock); + return PR_SUCCESS; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _PR_LOCK_LOCK(lock); + + /* Remove the lock from the owning thread's lock list */ + PR_REMOVE_LINK(&lock->links); + pri = lock->priority; + boost = lock->boostPriority; + if (boost > pri) { + /* + ** We received a priority boost during the time we held the lock. + ** We need to figure out what priority to move to by scanning + ** down our list of lock's that we are still holding and using + ** the highest boosted priority found. + */ + q = me->lockList.next; + while (q != &me->lockList) { + PRLock *ll = _PR_LOCK_PTR(q); + if (ll->boostPriority > pri) { + pri = ll->boostPriority; + } + q = q->next; + } + if (pri != me->priority) { + _PR_SetThreadPriority(me, pri); + } + } + + /* Assign the lock to the first waiting thread */ + q = lock->waitQ.next; + if (q == &lock->waitQ) { + /* Nobody wants the lock right now */ + lock->boostPriority = PR_PRIORITY_LOW; + lock->owner = 0; + } else { + if (_PR_AssignLock(lock) == NULL) { + /* no eligible thread to assign to */ + lock->boostPriority = PR_PRIORITY_LOW; + lock->owner = 0; + } + } + _PR_LOCK_UNLOCK(lock); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Test and then lock the lock if it's not already locked by some other +** thread. Return PR_FALSE if some other thread owned the lock at the +** time of the call. +*/ +PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRBool rv = PR_FALSE; + PRIntn is; + +#ifdef _PR_GLOBAL_THREADS_ONLY + is = _PR_MD_TEST_AND_LOCK(&lock->ilock); + if (is == 0) { + lock->owner = me; + return PR_TRUE; + } + return PR_FALSE; +#else /* _PR_GLOBAL_THREADS_ONLY */ + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + + _PR_LOCK_LOCK(lock); + if (lock->owner == 0) { + /* Just got the lock */ + lock->owner = me; + lock->priority = me->priority; + /* Add the granted lock to this owning thread's lock list */ + PR_APPEND_LINK(&lock->links, &me->lockList); + rv = PR_TRUE; + } + _PR_LOCK_UNLOCK(lock); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + return rv; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/************************************************************************/ +/************************************************************************/ +/***********************ROUTINES FOR DCE EMULATION***********************/ +/************************************************************************/ +/************************************************************************/ +PR_IMPLEMENT(PRStatus) PRP_TryLock(PRLock *lock) + { return (PR_TestAndLock(lock)) ? PR_SUCCESS : PR_FAILURE; } diff --git a/pr/src/threads/combined/prustack.c b/pr/src/threads/combined/prustack.c new file mode 100644 index 00000000..09690806 --- /dev/null +++ b/pr/src/threads/combined/prustack.c @@ -0,0 +1,183 @@ +/* -*- 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" + +#ifdef XP_UNIX +#include <sys/mman.h> +#endif + +/* List of free stack virtual memory chunks */ +PRLock *_pr_stackLock; +PRCList _pr_freeStacks = PR_INIT_STATIC_CLIST(&_pr_freeStacks); +PRIntn _pr_numFreeStacks; +PRIntn _pr_maxFreeStacks = 4; + +#ifdef DEBUG +/* +** A variable that can be set via the debugger... +*/ +PRBool _pr_debugStacks = PR_FALSE; +#endif + +/* How much space to leave between the stacks, at each end */ +#define REDZONE (2 << _pr_pageShift) + +#define _PR_THREAD_STACK_PTR(_qp) \ + ((PRThreadStack*) ((char*) (_qp) - offsetof(PRThreadStack,links))) + +void _PR_InitStacks(void) +{ + _pr_stackLock = PR_NewLock(); +} + +/* +** Allocate a stack for a thread. +*/ +PRThreadStack *_PR_NewStack(PRUint32 stackSize) +{ + PRCList *qp; + PRThreadStack *ts; + PRThread *thr; + + /* + ** Trim the list of free stacks. Trim it backwards, tossing out the + ** oldest stack found first (this way more recent stacks have a + ** chance of being present in the data cache). + */ + PR_Lock(_pr_stackLock); + qp = _pr_freeStacks.prev; + while ((_pr_numFreeStacks > _pr_maxFreeStacks) && (qp != &_pr_freeStacks)) { + ts = _PR_THREAD_STACK_PTR(qp); + thr = _PR_THREAD_STACK_TO_PTR(ts); + qp = qp->prev; + /* + * skip stacks which are still being used + */ + if (thr->no_sched) + continue; + PR_REMOVE_LINK(&ts->links); + + /* Give platform OS to clear out the stack for debugging */ + _PR_MD_CLEAR_STACK(ts); + + _pr_numFreeStacks--; + PR_DestroySegment(ts->seg); + PR_DELETE(ts); + } + + /* + ** Find a free thread stack. This searches the list of free'd up + ** virtually mapped thread stacks. + */ + qp = _pr_freeStacks.next; + ts = 0; + while (qp != &_pr_freeStacks) { + ts = _PR_THREAD_STACK_PTR(qp); + thr = _PR_THREAD_STACK_TO_PTR(ts); + qp = qp->next; + /* + * skip stacks which are still being used + */ + if ((!(thr->no_sched)) && ((ts->allocSize - 2*REDZONE) >= stackSize)) { + /* + ** Found a stack that is not in use and is big enough. Change + ** stackSize to fit it. + */ + stackSize = ts->allocSize - 2*REDZONE; + PR_REMOVE_LINK(&ts->links); + _pr_numFreeStacks--; + ts->links.next = 0; + ts->links.prev = 0; + PR_Unlock(_pr_stackLock); + goto done; + } + ts = 0; + } + PR_Unlock(_pr_stackLock); + + if (!ts) { + /* Make a new thread stack object. */ + ts = PR_NEWZAP(PRThreadStack); + if (!ts) { + return NULL; + } + + /* + ** Assign some of the virtual space to the new stack object. We + ** may not get that piece of VM, but if nothing else we will + ** advance the pointer so we don't collide (unless the OS screws + ** up). + */ + ts->allocSize = stackSize + 2*REDZONE; + ts->seg = PR_NewSegment(ts->allocSize, 0); + if (!ts->seg) { + PR_DELETE(ts); + return NULL; + } + } + + done: + ts->allocBase = ts->seg->vaddr; + ts->flags = _PR_STACK_MAPPED; + ts->stackSize = stackSize; + +#ifdef HAVE_STACK_GROWING_UP + ts->stackTop = ts->allocBase + REDZONE; + ts->stackBottom = ts->stackTop + stackSize; +#else + ts->stackBottom = ts->allocBase + REDZONE; + ts->stackTop = ts->stackBottom + stackSize; +#endif + + PR_LOG(_pr_thread_lm, PR_LOG_NOTICE, + ("thread stack: base=0x%x limit=0x%x bottom=0x%x top=0x%x\n", + ts->allocBase, ts->allocBase + ts->allocSize - 1, + ts->allocBase + REDZONE, + ts->allocBase + REDZONE + stackSize - 1)); + + _PR_MD_INIT_STACK(ts,REDZONE); + + return ts; +} + +/* +** Free the stack for the current thread +*/ +void _PR_FreeStack(PRThreadStack *ts) +{ + if (!ts) { + return; + } + if (ts->flags & _PR_STACK_PRIMORDIAL) { + PR_DELETE(ts); + return; + } + + /* + ** Put the stack on the free list. This is done because we are still + ** using the stack. Next time a thread is created we will trim the + ** list down; it's safe to do it then because we will have had to + ** context switch to a live stack before another thread can be + ** created. + */ + PR_Lock(_pr_stackLock); + PR_APPEND_LINK(&ts->links, _pr_freeStacks.prev); + _pr_numFreeStacks++; + PR_Unlock(_pr_stackLock); +} diff --git a/pr/src/threads/combined/pruthr.c b/pr/src/threads/combined/pruthr.c new file mode 100644 index 00000000..7baa6534 --- /dev/null +++ b/pr/src/threads/combined/pruthr.c @@ -0,0 +1,1794 @@ +/* -*- 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 <signal.h> +#include <string.h> + +/* _pr_activeLock protects the following global variables */ +PRLock *_pr_activeLock; +PRUintn _pr_primordialExitCount; /* In PR_Cleanup(), the primordial thread + * waits until all other user (non-system) + * threads have terminated before it exits. + * So whenever we decrement _pr_userActive, + * it is compared with + * _pr_primordialExitCount. + * If the primordial thread is a system + * thread, then _pr_primordialExitCount + * is 0. If the primordial thread is + * itself a user thread, then + * _pr_primordialThread is 1. + */ +PRCondVar *_pr_primordialExitCVar; /* When _pr_userActive is decremented to + * _pr_primordialExitCount, this condition + * variable is notified. + */ + +PRLock *_pr_deadQLock; +PRUint32 _pr_numNativeDead; +PRUint32 _pr_numUserDead; +PRCList _pr_deadNativeQ; +PRCList _pr_deadUserQ; + +PRUint32 _pr_join_counter; + +PRUint32 _pr_local_threads; +PRUint32 _pr_global_threads; + +PRBool suspendAllOn = PR_FALSE; +PRThread *suspendAllThread = NULL; + +extern PRCList _pr_active_global_threadQ; +extern PRCList _pr_active_local_threadQ; +extern _PRCPU *_pr_primordialCPU; + +static void _PR_DecrActiveThreadCount(PRThread *thread); +static PRThread *_PR_AttachThread(PRThreadType, PRThreadPriority, PRThreadStack *); +static void _PR_InitializeNativeStack(PRThreadStack *ts); +static void _PR_InitializeRecycledThread(PRThread *thread); + +void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, + PRUintn maxPTDs) +{ +#if defined(XP_MAC) +#pragma unused (maxPTDs) +#endif + + PRThread *thread; + PRThreadStack *stack; + + _pr_terminationCVLock = PR_NewLock(); + _pr_activeLock = PR_NewLock(); + +#ifndef HAVE_CUSTOM_USER_THREADS + stack = PR_NEWZAP(PRThreadStack); +#ifdef HAVE_STACK_GROWING_UP + stack->stackTop = (char*) ((((long)&type) >> _pr_pageShift) + << _pr_pageShift); +#else +#if defined(SOLARIS) || defined (UNIXWARE) && defined (USR_SVR4_THREADS) + stack->stackTop = (char*) &thread; +#else + stack->stackTop = (char*) ((((long)&type + _pr_pageSize - 1) + >> _pr_pageShift) << _pr_pageShift); +#endif +#endif +#else + /* If stack is NULL, we're using custom user threads like NT fibers. */ + stack = PR_NEWZAP(PRThreadStack); + if (stack) { + stack->stackSize = 0; + _PR_InitializeNativeStack(stack); + } +#endif /* HAVE_CUSTOM_USER_THREADS */ + + thread = _PR_AttachThread(type, priority, stack); + if (thread) { + _PR_MD_SET_CURRENT_THREAD(thread); + + if (type == PR_SYSTEM_THREAD) { + thread->flags = _PR_SYSTEM; + _pr_systemActive++; + _pr_primordialExitCount = 0; + } else { + _pr_userActive++; + _pr_primordialExitCount = 1; + } + thread->no_sched = 1; + _pr_primordialExitCVar = PR_NewCondVar(_pr_activeLock); + } + + if (!thread) PR_Abort(); +#ifdef _PR_GLOBAL_THREADS_ONLY + thread->flags |= _PR_PRIMORDIAL | _PR_GLOBAL_SCOPE; +#else + thread->flags |= _PR_PRIMORDIAL; +#endif + + /* Needs _PR_PRIMORDIAL flag set before calling _PR_MD_INIT_THREAD() */ + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + /* + * XXX do what? + */ + } + + if (_PR_IS_NATIVE_THREAD(thread)) { + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); + _pr_global_threads++; + } else { + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); + _pr_local_threads++; + } + + _pr_recycleThreads = 0; + _pr_deadQLock = PR_NewLock(); + _pr_numNativeDead = 0; + _pr_numUserDead = 0; + PR_INIT_CLIST(&_pr_deadNativeQ); + PR_INIT_CLIST(&_pr_deadUserQ); +} + +/* +** Initialize a stack for a native thread +*/ +static void _PR_InitializeNativeStack(PRThreadStack *ts) +{ + if( ts && (ts->stackTop == 0) ) { + ts->allocSize = ts->stackSize; + + /* + ** Setup stackTop and stackBottom values. + */ +#ifdef HAVE_STACK_GROWING_UP + ts->allocBase = (char*) ((((long)&ts) >> _pr_pageShift) + << _pr_pageShift); + ts->stackBottom = ts->allocBase + ts->stackSize; + ts->stackTop = ts->allocBase; +#else + ts->allocBase = (char*) ((((long)&ts + _pr_pageSize - 1) + >> _pr_pageShift) << _pr_pageShift); + ts->stackTop = ts->allocBase; + ts->stackBottom = ts->allocBase - ts->stackSize; +#endif + } +} + +void _PR_NotifyJoinWaiters(PRThread *thread) +{ + /* + ** Handle joinable threads. Change the state to waiting for join. + ** Remove from our run Q and put it on global waiting to join Q. + ** Notify on our "termination" condition variable so that joining + ** thread will know about our termination. Switch our context and + ** come back later on to continue the cleanup. + */ + PR_ASSERT(thread == _PR_MD_CURRENT_THREAD()); + if (thread->term != NULL) { + PR_Lock(_pr_terminationCVLock); + _PR_THREAD_LOCK(thread); + thread->state = _PR_JOIN_WAIT; + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_JOINQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + } + _PR_THREAD_UNLOCK(thread); + PR_NotifyCondVar(thread->term); + PR_Unlock(_pr_terminationCVLock); + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); + PR_ASSERT(thread->state != _PR_JOIN_WAIT); + } + +} + +/* + * Zero some of the data members of a recycled thread. + * + * Note that we can do this either when a dead thread is added to + * the dead thread queue or when it is reused. Here, we are doing + * this lazily, when the thread is reused in _PR_CreateThread(). + */ +static void _PR_InitializeRecycledThread(PRThread *thread) +{ + /* + * Assert that the following data members are already zeroed + * by _PR_CleanupThread(). + */ +#ifdef DEBUG + if (thread->privateData) { + unsigned int i; + for (i = 0; i < thread->tpdLength; i++) { + PR_ASSERT(thread->privateData[i] == NULL); + } + } +#endif + PR_ASSERT(thread->dumpArg == 0 && thread->dump == 0); + PR_ASSERT(thread->numExits == 0 && thread->ptes == 0); + PR_ASSERT(thread->errorString == 0 && thread->errorStringSize == 0); + + /* Reset data members in thread structure */ + thread->errorCode = thread->osErrorCode = 0; + thread->io_pending = thread->io_suspended = PR_FALSE; + thread->environment = 0; + PR_INIT_CLIST(&thread->lockList); +} + +PRStatus _PR_RecycleThread(PRThread *thread) +{ + if ( _PR_IS_NATIVE_THREAD(thread) && + _PR_NUM_DEADNATIVE < _pr_recycleThreads) { + _PR_DEADQ_LOCK; + PR_APPEND_LINK(&thread->links, &_PR_DEADNATIVEQ); + _PR_INC_DEADNATIVE; + _PR_DEADQ_UNLOCK; + return (PR_SUCCESS); + } else if ( !_PR_IS_NATIVE_THREAD(thread) && + _PR_NUM_DEADUSER < _pr_recycleThreads) { + _PR_DEADQ_LOCK; + PR_APPEND_LINK(&thread->links, &_PR_DEADUSERQ); + _PR_INC_DEADUSER; + _PR_DEADQ_UNLOCK; + return (PR_SUCCESS); + } + return (PR_FAILURE); +} + +/* + * Decrement the active thread count, either _pr_systemActive or + * _pr_userActive, depending on whether the thread is a system thread + * or a user thread. If all the user threads, except possibly + * the primordial thread, have terminated, we notify the primordial + * thread of this condition. + * + * Since this function will lock _pr_activeLock, do not call this + * function while holding the _pr_activeLock lock, as this will result + * in a deadlock. + */ + +static void +_PR_DecrActiveThreadCount(PRThread *thread) +{ + PR_Lock(_pr_activeLock); + if (thread->flags & _PR_SYSTEM) { + _pr_systemActive--; + } else { + _pr_userActive--; + if (_pr_userActive == _pr_primordialExitCount) { + PR_NotifyCondVar(_pr_primordialExitCVar); + } + } + PR_Unlock(_pr_activeLock); +} + +/* +** Detach thread structure +*/ +static void +_PR_DetachThread(PRThread *thread) +{ + _MD_FREE_LOCK(&thread->threadLock); + PR_DELETE(thread); +} + +void +_PR_NativeDestroyThread(PRThread *thread) +{ + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + + PR_DELETE(thread->stack); + _PR_DetachThread(thread); +} + +void +_PR_UserDestroyThread(PRThread *thread) +{ + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + if (NULL != thread->privateData) + { + PR_ASSERT(0 != thread->tpdLength); + PR_DELETE(thread->privateData); + thread->privateData = NULL; + thread->tpdLength = 0; + } + _MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) { + _PR_MD_CLEAN_THREAD(thread); + /* + * Because the no_sched field is set, this thread/stack will + * will not be re-used until the flag is cleared by the thread + * we will context switch to. + */ + _PR_FreeStack(thread->stack); + } else { +#ifdef WINNT + _PR_MD_CLEAN_THREAD(thread); +#else + /* + * This assertion does not apply to NT. On NT, every fiber, + * including the primordial thread, has its threadAllocatedOnStack + * equal to 0. Elsewhere, only the primordial thread has its + * threadAllocatedOnStack equal to 0. + */ + PR_ASSERT(thread->flags & _PR_PRIMORDIAL); +#endif + } +} + + +/* +** Run a thread's start function. When the start function returns the +** thread is done executing and no longer needs the CPU. If there are no +** more user threads running then we can exit the program. +*/ +void _PR_NativeRunThread(void *arg) +{ + PRThread *thread = (PRThread *)arg; + + _PR_MD_SET_CURRENT_THREAD(thread); + + _PR_MD_SET_CURRENT_CPU(NULL); + + /* Set up the thread stack information */ + _PR_InitializeNativeStack(thread->stack); + + /* Set up the thread md information */ + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + /* + * thread failed to initialize itself, possibly due to + * failure to allocate per-thread resources + */ + return; + } + + while(1) { + thread->state = _PR_RUNNING; + + if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_SET_INTSOFF(0); + + /* + * Add to list of active threads + */ + PR_Lock(_pr_activeLock); + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_GLOBAL_THREADQ()); + _pr_global_threads++; + PR_Unlock(_pr_activeLock); + + (*thread->startFunc)(thread->arg); + + /* + * The following two assertions are meant for NT asynch io. + * + * The thread should have no asynch io in progress when it + * exits, otherwise the overlapped buffer, which is part of + * the thread structure, would become invalid. + */ + PR_ASSERT(thread->io_pending == PR_FALSE); + /* + * This assertion enforces the programming guideline that + * if an io function times out or is interrupted, the thread + * should close the fd to force the asynch io to abort + * before it exits. Right now, closing the fd is the only + * way to clear the io_suspended flag. + */ + PR_ASSERT(thread->io_suspended == PR_FALSE); + + /* + * remove thread from list of active threads + */ + PR_Lock(_pr_activeLock); + PR_REMOVE_LINK(&thread->active); + _pr_global_threads--; + PR_Unlock(_pr_activeLock); + + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); + + /* All done, time to go away */ + _PR_CleanupThread(thread); + + _PR_NotifyJoinWaiters(thread); + + _PR_DecrActiveThreadCount(thread); + + thread->state = _PR_DEAD_STATE; + + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == + PR_FAILURE)) { + /* + * thread not recycled + * platform-specific thread exit processing + * - for stuff like releasing native-thread resources, etc. + */ + _PR_MD_EXIT_THREAD(thread); + /* + * Free memory allocated for the thread + */ + _PR_NativeDestroyThread(thread); + /* + * thread gone, cannot de-reference thread now + */ + return; + } + + /* Now wait for someone to activate us again... */ + _PR_MD_WAIT(thread, PR_INTERVAL_NO_TIMEOUT); + } +} + +void _PR_UserRunThread(void) +{ + PRThread *thread = _PR_MD_CURRENT_THREAD(); + PRIntn is; + + if (_MD_LAST_THREAD()) + _MD_LAST_THREAD()->no_sched = 0; + +#ifdef HAVE_CUSTOM_USER_THREADS + if (thread->stack == NULL) { + thread->stack = PR_NEWZAP(PRThreadStack); + _PR_InitializeNativeStack(thread->stack); + } +#endif /* HAVE_CUSTOM_USER_THREADS */ + + while(1) { + /* Run thread main */ + if ( !_PR_IS_NATIVE_THREAD(thread)) _PR_SET_INTSOFF(0); + + /* + * Add to list of active threads + */ + if (!(thread->flags & _PR_IDLE_THREAD)) { + PR_Lock(_pr_activeLock); + PR_APPEND_LINK(&thread->active, &_PR_ACTIVE_LOCAL_THREADQ()); + _pr_local_threads++; + PR_Unlock(_pr_activeLock); + } + + (*thread->startFunc)(thread->arg); + + /* + * The following two assertions are meant for NT asynch io. + * + * The thread should have no asynch io in progress when it + * exits, otherwise the overlapped buffer, which is part of + * the thread structure, would become invalid. + */ + PR_ASSERT(thread->io_pending == PR_FALSE); + /* + * This assertion enforces the programming guideline that + * if an io function times out or is interrupted, the thread + * should close the fd to force the asynch io to abort + * before it exits. Right now, closing the fd is the only + * way to clear the io_suspended flag. + */ + PR_ASSERT(thread->io_suspended == PR_FALSE); + + PR_Lock(_pr_activeLock); + /* + * remove thread from list of active threads + */ + if (!(thread->flags & _PR_IDLE_THREAD)) { + PR_REMOVE_LINK(&thread->active); + _pr_local_threads--; + } + PR_Unlock(_pr_activeLock); + PR_LOG(_pr_thread_lm, PR_LOG_MIN, ("thread exiting")); + + /* All done, time to go away */ + _PR_CleanupThread(thread); + + _PR_INTSOFF(is); + + _PR_NotifyJoinWaiters(thread); + + _PR_DecrActiveThreadCount(thread); + + thread->state = _PR_DEAD_STATE; + + if (!_pr_recycleThreads || (_PR_RecycleThread(thread) == + PR_FAILURE)) { + /* + ** Destroy the thread resources + */ + _PR_UserDestroyThread(thread); + } + + /* + ** Find another user thread to run. This cpu has finished the + ** previous threads main and is now ready to run another thread. + */ + { + PRInt32 is; + _PR_INTSOFF(is); + _PR_MD_SWITCH_CONTEXT(thread); + } + + /* Will land here when we get scheduled again if we are recycling... */ + } +} + +void _PR_SetThreadPriority(PRThread *thread, PRThreadPriority newPri) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRIntn is; + + if ( _PR_IS_NATIVE_THREAD(thread) ) { + _PR_MD_SET_PRIORITY(&(thread->md), newPri); + return; + } + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _PR_THREAD_LOCK(thread); + if (newPri != thread->priority) { + PRUintn oldPri; + _PRCPU *cpu = thread->cpu; + + oldPri = thread->priority; + switch (thread->state) { + case _PR_RUNNING: + /* Change my priority */ + + _PR_RUNQ_LOCK(cpu); + thread->priority = newPri; + if (_PR_RUNQREADYMASK(cpu) >> (newPri + 1)) { + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_SET_RESCHED_FLAG(); + } + _PR_RUNQ_UNLOCK(cpu); + break; + + case _PR_RUNNABLE: + + _PR_RUNQ_LOCK(cpu); + /* Move to different runQ */ + _PR_DEL_RUNQ(thread); + thread->priority = newPri; + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + _PR_ADD_RUNQ(thread, cpu, newPri); + _PR_RUNQ_UNLOCK(cpu); + + if (newPri > me->priority) { + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_SET_RESCHED_FLAG(); + } + + break; + + case _PR_LOCK_WAIT: + case _PR_COND_WAIT: + case _PR_IO_WAIT: + case _PR_SUSPENDED: + + thread->priority = newPri; + break; + } + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); +} + +/* +** Suspend the named thread and copy its gc registers into regBuf +*/ +static void _PR_Suspend(PRThread *thread) +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(thread != me); + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread) || (!thread->cpu)); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _PR_THREAD_LOCK(thread); + switch (thread->state) { + case _PR_RUNNABLE: + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_RUNQ_LOCK(thread->cpu); + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(thread->cpu); + + _PR_MISCQ_LOCK(thread->cpu); + _PR_ADD_SUSPENDQ(thread, thread->cpu); + _PR_MISCQ_UNLOCK(thread->cpu); + } else { + /* + * Only LOCAL threads are suspended by _PR_Suspend + */ + PR_ASSERT(0); + } + thread->state = _PR_SUSPENDED; + break; + + case _PR_RUNNING: + /* + * The thread being suspended should be a LOCAL thread with + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state + */ + PR_ASSERT(0); + break; + + case _PR_LOCK_WAIT: + case _PR_IO_WAIT: + case _PR_COND_WAIT: + if (_PR_IS_NATIVE_THREAD(thread)) { + _PR_MD_SUSPEND_THREAD(thread); + } + thread->flags |= _PR_SUSPENDING; + break; + + default: + PR_Abort(); + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); +} + +static void _PR_Resume(PRThread *thread) +{ + PRThreadPriority pri; + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + _PR_THREAD_LOCK(thread); + switch (thread->state) { + case _PR_SUSPENDED: + thread->state = _PR_RUNNABLE; + thread->flags &= ~_PR_SUSPENDING; + if (!_PR_IS_NATIVE_THREAD(thread)) { + _PR_MISCQ_LOCK(thread->cpu); + _PR_DEL_SUSPENDQ(thread); + _PR_MISCQ_UNLOCK(thread->cpu); + + pri = thread->priority; + + _PR_RUNQ_LOCK(thread->cpu); + _PR_ADD_RUNQ(thread, thread->cpu, pri); + _PR_RUNQ_UNLOCK(thread->cpu); + + if (pri > _PR_MD_CURRENT_THREAD()->priority) { + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_SET_RESCHED_FLAG(); + } + } else { + PR_ASSERT(0); + } + break; + + case _PR_IO_WAIT: + case _PR_COND_WAIT: + thread->flags &= ~_PR_SUSPENDING; +/* PR_ASSERT(thread->wait.monitor->stickyCount == 0); */ + break; + + case _PR_LOCK_WAIT: + { + PRLock *wLock = thread->wait.lock; + + thread->flags &= ~_PR_SUSPENDING; + + _PR_LOCK_LOCK(wLock); + if (thread->wait.lock->owner == 0) { + _PR_AssignLock(thread->wait.lock); + } + _PR_LOCK_UNLOCK(wLock); + break; + } + case _PR_RUNNABLE: + break; + case _PR_RUNNING: + /* + * The thread being suspended should be a LOCAL thread with + * _pr_numCPUs == 1. Hence, the thread cannot be in RUNNING state + */ + PR_ASSERT(0); + break; + + default: + /* + * thread should have been in one of the above-listed blocked states + * (_PR_JOIN_WAIT, _PR_IO_WAIT, _PR_UNBORN, _PR_DEAD_STATE) + */ + PR_Abort(); + } + _PR_THREAD_UNLOCK(thread); + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + +} + +static PRThread *get_thread(_PRCPU *cpu) +{ + PRThread *thread; + PRIntn pri; + PRUint32 r; + PRCList *qp; + PRIntn priMin, priMax; + + _PR_RUNQ_LOCK(cpu); + r = _PR_RUNQREADYMASK(cpu); + if (r==0) { + priMin = priMax = PR_PRIORITY_FIRST; + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { + priMin = priMax = PR_PRIORITY_NORMAL; + } else { + priMin = PR_PRIORITY_FIRST; + priMax = PR_PRIORITY_LAST; + } + thread = NULL; + for (pri = priMax; pri >= priMin ; pri-- ) { + if (r & (1 << pri)) { + for (qp = _PR_RUNQ(cpu)[pri].next; + qp != &_PR_RUNQ(cpu)[pri]; + qp = qp->next) { + thread = _PR_THREAD_PTR(qp); + /* + * skip non-schedulable threads + */ + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); + if (thread->no_sched){ + thread = NULL; + /* + * Need to wakeup cpus to avoid missing a + * runnable thread + */ + _PR_MD_WAKEUP_CPUS(); + continue; + } else if (thread->io_pending == PR_TRUE) { + /* + * A thread that is blocked for I/O needs to run + * on the same cpu on which it was blocked + */ + thread = NULL; + continue; + } else { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + return(thread); + } + } + } + thread = NULL; + } + _PR_RUNQ_UNLOCK(cpu); + return(thread); +} + +/* +** Schedule this native thread by finding the highest priority nspr +** thread that is ready to run. +** +** Note- everyone really needs to call _PR_MD_SWITCH_CONTEXT (which calls +** PR_Schedule() rather than calling PR_Schedule. Otherwise if there +** is initialization required for switching from SWITCH_CONTEXT, +** it will not get done! +*/ +void _PR_Schedule(void) +{ + PRThread *thread, *me = _PR_MD_CURRENT_THREAD(); + _PRCPU *cpu = me->cpu; + PRIntn pri; + PRUint32 r; + PRCList *qp; + PRIntn priMin, priMax; + + /* Interrupts must be disabled */ + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || _PR_MD_GET_INTSOFF() != 0); + + /* Since we are rescheduling, we no longer want to */ + _PR_CLEAR_RESCHED_FLAG(); + + /* + ** Find highest priority thread to run. Bigger priority numbers are + ** higher priority threads + */ + _PR_RUNQ_LOCK(cpu); + /* + * if we are in SuspendAll mode, can schedule only the thread + * that called PR_SuspendAll + * + * The thread may be ready to run now, after completing an I/O + * operation, for example + */ + if ((thread = suspendAllThread) != 0) { + if ((!(thread->no_sched)) && (thread->state == _PR_RUNNABLE)) { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + goto found_thread; + } else { + thread = NULL; + _PR_RUNQ_UNLOCK(cpu); + goto idle_thread; + } + } + r = _PR_RUNQREADYMASK(cpu); + if (r==0) { + priMin = priMax = PR_PRIORITY_FIRST; + } else if (r == (1<<PR_PRIORITY_NORMAL) ) { + priMin = priMax = PR_PRIORITY_NORMAL; + } else { + priMin = PR_PRIORITY_FIRST; + priMax = PR_PRIORITY_LAST; + } + thread = NULL; + for (pri = priMax; pri >= priMin ; pri-- ) { + if (r & (1 << pri)) { + for (qp = _PR_RUNQ(cpu)[pri].next; + qp != &_PR_RUNQ(cpu)[pri]; + qp = qp->next) { + thread = _PR_THREAD_PTR(qp); + /* + * skip non-schedulable threads + */ +#if !defined(XP_MAC) + PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); +#endif + if ((thread->no_sched) && (me != thread)){ + thread = NULL; + continue; + } else { + /* Pull thread off of its run queue */ + _PR_DEL_RUNQ(thread); + _PR_RUNQ_UNLOCK(cpu); + goto found_thread; + } + } + } + thread = NULL; + } + _PR_RUNQ_UNLOCK(cpu); + +#if !defined(_PR_LOCAL_THREADS_ONLY) && defined(XP_UNIX) + _PR_CPU_LIST_LOCK(); + for (qp = _PR_CPUQ().next; qp != &_PR_CPUQ(); qp = qp->next) { + if (cpu != _PR_CPU_PTR(qp)) { + if ((thread = get_thread(_PR_CPU_PTR(qp))) != NULL) { + thread->cpu = cpu; + _PR_CPU_LIST_UNLOCK(); + goto found_thread; + } + } + } + _PR_CPU_LIST_UNLOCK(); +#endif /* _PR_LOCAL_THREADS_ONLY */ + +idle_thread: + /* + ** There are no threads to run. Switch to the idle thread + */ + PR_LOG(_pr_sched_lm, PR_LOG_MAX, ("pausing")); + thread = _PR_MD_CURRENT_CPU()->idle_thread; + +found_thread: + PR_ASSERT((me == thread) || ((thread->state == _PR_RUNNABLE) && + (!(thread->no_sched)))); + + /* Resume the thread */ + PR_LOG(_pr_sched_lm, PR_LOG_MAX, + ("switching to %d[%p]", thread->id, thread)); + PR_ASSERT(thread->state != _PR_RUNNING); + thread->state = _PR_RUNNING; + + /* If we are on the runq, it just means that we went to sleep on some + * resource, and by the time we got here another real native thread had + * already given us the resource and put us back on the runqueue + */ + if (thread != me) + _PR_MD_RESTORE_CONTEXT(thread); +#if 0 + /* XXXMB; with setjmp/longjmp it is impossible to land here, but + * it is not with fibers... Is this a bad thing? I believe it is + * still safe. + */ + PR_NOT_REACHED("impossible return from schedule"); +#endif +} + +/* +** Attaches a thread. +** Does not set the _PR_MD_CURRENT_THREAD. +** Does not specify the scope of the thread. +*/ +static PRThread * +_PR_AttachThread(PRThreadType type, PRThreadPriority priority, + PRThreadStack *stack) +{ +#if defined(XP_MAC) +#pragma unused (type) +#endif + + PRThread *thread; + char *mem; + + if (priority > PR_PRIORITY_LAST) { + priority = PR_PRIORITY_LAST; + } else if (priority < PR_PRIORITY_FIRST) { + priority = PR_PRIORITY_FIRST; + } + + mem = (char*) PR_CALLOC(sizeof(PRThread)); + if (mem) { + thread = (PRThread*) mem; + thread->priority = priority; + thread->stack = stack; + thread->state = _PR_RUNNING; + PR_INIT_CLIST(&thread->lockList); + if (_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { + PR_DELETE(thread); + return 0; + } + + return thread; + } + return 0; +} + + + +PR_IMPLEMENT(PRThread*) +_PR_NativeCreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, + PRUint32 flags) +{ +#if defined(XP_MAC) +#pragma unused (scope) +#endif + + PRThread *thread; + + thread = _PR_AttachThread(type, priority, NULL); + + if (thread) { + PR_Lock(_pr_activeLock); + thread->flags = (flags | _PR_GLOBAL_SCOPE); + thread->id = ++_pr_utid; + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + PR_Unlock(_pr_activeLock); + + thread->stack = PR_NEWZAP(PRThreadStack); + if (!thread->stack) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + goto done; + } + thread->stack->stackSize = stackSize?stackSize:_MD_DEFAULT_STACK_SIZE; + thread->stack->thr = thread; + thread->startFunc = start; + thread->arg = arg; + + /* + Set thread flags related to scope and joinable state. If joinable + thread, allocate a "termination" conidition variable. + */ + if (state == PR_JOINABLE_THREAD) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + if (thread->term == NULL) { + PR_DELETE(thread->stack); + goto done; + } + } + + thread->state = _PR_RUNNING; + if (_PR_MD_CREATE_THREAD(thread, _PR_NativeRunThread, priority, + scope,state,stackSize) == PR_SUCCESS) { + return thread; + } + if (thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = NULL; + } + PR_DELETE(thread->stack); + } + +done: + if (thread) { + _PR_DecrActiveThreadCount(thread); + _PR_DetachThread(thread); + } + return NULL; +} + +/************************************************************************/ + +PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize, + PRUint32 flags) +{ + PRThread *me; + PRThread *thread = NULL; + PRThreadStack *stack; + char *top; + PRIntn is; + PRIntn native = 0; + PRIntn useRecycled = 0; + PRBool status; + + /* + First, pin down the priority. Not all compilers catch passing out of + range enum here. If we let bad values thru, priority queues won't work. + */ + if (priority > PR_PRIORITY_LAST) { + priority = PR_PRIORITY_LAST; + } else if (priority < PR_PRIORITY_FIRST) { + priority = PR_PRIORITY_FIRST; + } + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (! (flags & _PR_IDLE_THREAD)) + me = _PR_MD_CURRENT_THREAD(); + +#if defined(_PR_GLOBAL_THREADS_ONLY) + scope = PR_GLOBAL_THREAD; +#endif + + native = ((scope == PR_GLOBAL_THREAD) && _PR_IS_NATIVE_THREAD_SUPPORTED()); + + _PR_ADJUST_STACKSIZE(stackSize); + + if (native) { + /* + * clear the IDLE_THREAD flag which applies to LOCAL + * threads only + */ + flags &= ~_PR_IDLE_THREAD; + flags |= _PR_GLOBAL_SCOPE; + if (_PR_NUM_DEADNATIVE > 0) { + _PR_DEADQ_LOCK; + + if (_PR_NUM_DEADNATIVE == 0) { /* Thread safe check */ + _PR_DEADQ_UNLOCK; + } else { + thread = _PR_THREAD_PTR(_PR_DEADNATIVEQ.next); + PR_REMOVE_LINK(&thread->links); + _PR_DEC_DEADNATIVE; + _PR_DEADQ_UNLOCK; + + _PR_InitializeRecycledThread(thread); + thread->startFunc = start; + thread->arg = arg; + PR_Lock(_pr_activeLock); + thread->flags = (flags | _PR_GLOBAL_SCOPE); + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + PR_Unlock(_pr_activeLock); + + if (state == PR_JOINABLE_THREAD) { + if (!thread->term) + thread->term = PR_NewCondVar(_pr_terminationCVLock); + } + else { + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + } + + thread->priority = priority; + _PR_MD_SET_PRIORITY(&(thread->md), priority); + /* XXX what about stackSize? */ + thread->state = _PR_RUNNING; + _PR_MD_WAKEUP_WAITER(thread); + return thread; + } + } + thread = _PR_NativeCreateThread(type, start, arg, priority, + scope, state, stackSize, flags); + } else { + if (_PR_NUM_DEADUSER > 0) { + _PR_DEADQ_LOCK; + + if (_PR_NUM_DEADUSER == 0) { /* thread safe check */ + _PR_DEADQ_UNLOCK; + } else { + PRCList *ptr; + + /* Go down list checking for a recycled thread with a + * large enough stack. XXXMB - this has a bad degenerate case. + */ + ptr = _PR_DEADUSERQ.next; + while( ptr != &_PR_DEADUSERQ ) { + thread = _PR_THREAD_PTR(ptr); + if ((thread->stack->stackSize >= stackSize) && + (!thread->no_sched)) { + PR_REMOVE_LINK(&thread->links); + _PR_DEC_DEADUSER; + break; + } else { + ptr = ptr->next; + thread = NULL; + } + } + + _PR_DEADQ_UNLOCK; + + if (thread) { + _PR_InitializeRecycledThread(thread); + thread->startFunc = start; + thread->arg = arg; + thread->priority = priority; + if (state == PR_JOINABLE_THREAD) { + if (!thread->term) + thread->term = PR_NewCondVar(_pr_terminationCVLock); + } else { + if(thread->term) { + PR_DestroyCondVar(thread->term); + thread->term = 0; + } + } + useRecycled++; + } + } + } + if (thread == NULL) { +#ifndef HAVE_CUSTOM_USER_THREADS + stack = _PR_NewStack(stackSize); + if (!stack) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return 0; + } + stack->thr = thread; + + /* Allocate thread object and per-thread data off the top of the stack*/ + top = stack->stackTop; +#ifdef HAVE_STACK_GROWING_UP + thread = (PRThread*) top; + top = top + sizeof(PRThread); + /* + * Make stack 64-byte aligned + */ + if ((PRUptrdiff)top & 0x3f) { + top = (char*)(((PRUptrdiff)top + 0x40) & ~0x3f); + } +#else + top = top - sizeof(PRThread); + thread = (PRThread*) top; + /* + * Make stack 64-byte aligned + */ + if ((PRUptrdiff)top & 0x3f) { + top = (char*)((PRUptrdiff)top & ~0x3f); + } +#endif + memset(thread, 0, sizeof(PRThread)); + thread->threadAllocatedOnStack = 1; +#else + thread = _PR_MD_CREATE_USER_THREAD(stackSize, start, arg); + if (!thread) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + thread->threadAllocatedOnStack = 0; + stack = NULL; +#endif + + /* Initialize thread */ + if (stack) + stack->thr = thread; + thread->tpdLength = 0; + thread->privateData = NULL; + thread->stack = stack; + thread->priority = priority; + thread->startFunc = start; + thread->arg = arg; + PR_INIT_CLIST(&thread->lockList); + + if (_PR_MD_INIT_THREAD(thread) == PR_FAILURE) { + if (thread->threadAllocatedOnStack == 1) + _PR_FreeStack(thread->stack); + else { + PR_DELETE(thread); + } + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } + + if (_MD_NEW_LOCK(&thread->threadLock) == PR_FAILURE) { + if (thread->threadAllocatedOnStack == 1) + _PR_FreeStack(thread->stack); + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); + return NULL; + } + + _PR_MD_INIT_CONTEXT(thread, top, _PR_UserRunThread, &status); + + if (status == PR_FALSE) { + _MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) + _PR_FreeStack(thread->stack); + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + return NULL; + } + + /* + Set thread flags related to scope and joinable state. If joinable + thread, allocate a "termination" condition variable. + */ + if (state == PR_JOINABLE_THREAD) { + thread->term = PR_NewCondVar(_pr_terminationCVLock); + if (thread->term == NULL) { + _MD_FREE_LOCK(&thread->threadLock); + if (thread->threadAllocatedOnStack == 1) + _PR_FreeStack(thread->stack); + else { + PR_DELETE(thread->privateData); + PR_DELETE(thread); + } + return NULL; + } + } + + } + + /* Update thread type counter */ + PR_Lock(_pr_activeLock); + thread->flags = flags; + thread->id = ++_pr_utid; + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + + /* Make thread runnable */ + thread->state = _PR_RUNNABLE; + /* + * Add to list of active threads + */ + PR_Unlock(_pr_activeLock); + + if ( (thread->flags & _PR_IDLE_THREAD) || _PR_IS_NATIVE_THREAD(me) ) + thread->cpu = _PR_GetPrimordialCPU(); + else + thread->cpu = _PR_MD_CURRENT_CPU(); + + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(thread)) { + _PR_RUNQ_LOCK(thread->cpu); + _PR_ADD_RUNQ(thread, thread->cpu, priority); + _PR_RUNQ_UNLOCK(thread->cpu); + } + + if ((thread->flags & _PR_IDLE_THREAD) || _PR_IS_NATIVE_THREAD(me)) { + /* + ** If the creating thread is a kernel thread, we need to + ** awaken the user thread idle thread somehow; potentially + ** it could be sleeping in its idle loop, and we need to poke + ** it. To do so, wake the idle thread... + */ + _PR_MD_WAKEUP_WAITER(NULL); + } + if ((! (thread->flags & _PR_IDLE_THREAD)) && !_PR_IS_NATIVE_THREAD(me) ) + _PR_INTSON(is); + } + + return thread; +} + +PR_IMPLEMENT(PRThread*) PR_CreateThread(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, 0); +} + +/* +** Associate a thread object with an existing native thread. +** "type" is the type of thread object to attach +** "priority" is the priority to assign to the thread +** "stack" defines the shape of the threads stack +** +** This can return NULL if some kind of error occurs, or if memory is +** tight. +** +** This call is not normally needed unless you create your own native +** thread. PR_Init does this automatically for the primordial thread. +*/ +PR_IMPLEMENT(PRThread*) _PRI_AttachThread(PRThreadType type, + PRThreadPriority priority, PRThreadStack *stack, PRUint32 flags) +{ + PRThread *thread; + + if ((thread = _PR_MD_GET_ATTACHED_THREAD()) != NULL) { + return thread; + } + + /* Clear out any state if this thread was attached before */ + _PR_MD_SET_CURRENT_CPU(NULL); + + thread = _PR_AttachThread(type, priority, stack); + if (thread) { + PRIntn is; + + _PR_MD_SET_CURRENT_THREAD(thread); + + thread->flags = flags | _PR_GLOBAL_SCOPE | _PR_ATTACHED; + + if (!stack) { + thread->stack = PR_NEWZAP(PRThreadStack); + if (!thread->stack) { + _PR_DetachThread(thread); + return NULL; + } + thread->stack->stackSize = _MD_DEFAULT_STACK_SIZE; + } + PR_INIT_CLIST(&thread->links); + + if (_PR_MD_INIT_ATTACHED_THREAD(thread) == PR_FAILURE) { + PR_DELETE(thread->stack); + _PR_DetachThread(thread); + return NULL; + } + + _PR_MD_SET_CURRENT_CPU(NULL); + + if (_PR_MD_CURRENT_CPU()) { + _PR_INTSOFF(is); + PR_Lock(_pr_activeLock); + } + if (type == PR_SYSTEM_THREAD) { + thread->flags |= _PR_SYSTEM; + _pr_systemActive++; + } else { + _pr_userActive++; + } + if (_PR_MD_CURRENT_CPU()) { + PR_Unlock(_pr_activeLock); + _PR_INTSON(is); + } + } + return thread; +} + +PR_IMPLEMENT(PRThread*) PR_AttachThread(PRThreadType type, + PRThreadPriority priority, PRThreadStack *stack) +{ + return _PRI_AttachThread(type, priority, stack, 0); +} + +/* +** Detach the nspr thread from the currently executing native thread. +** The thread object will be destroyed and all related data attached +** to it. The exit procs will be invoked. +** +** This call is not normally needed unless you create your own native +** thread. PR_Exit will automatially detach the nspr thread object +** created by PR_Init for the primordial thread. +** +** This call returns after the nspr thread object is destroyed. +*/ +PR_IMPLEMENT(void) PR_DetachThread(void) +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + PR_ASSERT(me->flags & _PR_ATTACHED); + _PR_CleanupThread(me); + PR_DELETE(me->privateData); + _PR_MD_CLEAN_THREAD(me); + + /*XXX we need DetachCPU */ + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); + _PR_DecrActiveThreadCount(me); + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_FAST_INTSON(is); + _PR_MD_SET_CURRENT_THREAD(NULL); + if (!me->threadAllocatedOnStack) + PR_DELETE(me->stack); + _MD_FREE_LOCK(&me->threadLock); + PR_DELETE(me); +} + +/* +** Wait for thread termination: +** "thread" is the target thread +** +** This can return PR_FAILURE if no joinable thread could be found +** corresponding to the specified target thread. +** +** The calling thread is suspended until the target thread completes. +** Several threads cannot wait for the same thread to complete; one thread +** will complete successfully and others will terminate with an error PR_FAILURE. +** The calling thread will not be blocked if the target thread has already +** terminated. +*/ +PR_IMPLEMENT(PRStatus) PR_JoinThread(PRThread *thread) +{ + PRIntn is; + PRCondVar *term; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + term = thread->term; + /* can't join a non-joinable thread */ + if (term == NULL) { + goto ErrorExit; + } + + /* multiple threads can't wait on the same joinable thread */ + if (term->condQ.next != &term->condQ) { + goto ErrorExit; + } + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + + /* wait for the target thread's termination cv invariant */ + PR_Lock (_pr_terminationCVLock); + while (thread->state != _PR_JOIN_WAIT) { + (void) PR_WaitCondVar(term, PR_INTERVAL_NO_TIMEOUT); + } + (void) PR_Unlock (_pr_terminationCVLock); + + /* + Remove target thread from global waiting to join Q; make it runnable + again and put it back on its run Q. When it gets scheduled later in + _PR_RunThread code, it will clean up its stack. + */ + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + thread->state = _PR_RUNNABLE; + if ( !_PR_IS_NATIVE_THREAD(thread) ) { + _PR_THREAD_LOCK(thread); + + _PR_MISCQ_LOCK(thread->cpu); + _PR_DEL_JOINQ(thread); + _PR_MISCQ_UNLOCK(thread->cpu); + + _PR_AddThreadToRunQ(me, thread); + _PR_THREAD_UNLOCK(thread); + } + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + + _PR_MD_WAKEUP_WAITER(thread); + + return PR_SUCCESS; + +ErrorExit: + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); + return PR_FAILURE; +} + +PR_IMPLEMENT(void) PR_SetThreadPriority(PRThread *thread, + PRThreadPriority newPri) +{ + + /* + First, pin down the priority. Not all compilers catch passing out of + range enum here. If we let bad values thru, priority queues won't work. + */ + if (newPri > PR_PRIORITY_LAST) { + newPri = PR_PRIORITY_LAST; + } else if (newPri < PR_PRIORITY_FIRST) { + newPri = PR_PRIORITY_FIRST; + } + + if ( _PR_IS_NATIVE_THREAD(thread) ) { + thread->priority = newPri; + _PR_MD_SET_PRIORITY(&(thread->md), newPri); + } else _PR_SetThreadPriority(thread, newPri); +} + + +/* +** This routine prevents all other threads from running. This call is needed by +** the garbage collector. +*/ +PR_IMPLEMENT(void) PR_SuspendAll(void) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCList *qp; + + /* + * Stop all user and native threads which are marked GC able. + */ + PR_Lock(_pr_activeLock); + suspendAllOn = PR_TRUE; + suspendAllThread = _PR_MD_CURRENT_THREAD(); + _PR_MD_BEGIN_SUSPEND_ALL(); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD)) { + _PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); + PR_ASSERT((_PR_ACTIVE_THREAD_PTR(qp))->state != _PR_RUNNING); + } + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD)) + /* PR_Suspend(_PR_ACTIVE_THREAD_PTR(qp)); */ + _PR_MD_SUSPEND_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); + } + _PR_MD_END_SUSPEND_ALL(); +} + +/* +** This routine unblocks all other threads that were suspended from running by +** PR_SuspendAll(). This call is needed by the garbage collector. +*/ +PR_IMPLEMENT(void) PR_ResumeAll(void) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRCList *qp; + + /* + * Resume all user and native threads which are marked GC able. + */ + _PR_MD_BEGIN_RESUME_ALL(); + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD)) + _PR_Resume(_PR_ACTIVE_THREAD_PTR(qp)); + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp->next) { + if ((me != _PR_ACTIVE_THREAD_PTR(qp)) && + (_PR_ACTIVE_THREAD_PTR(qp)->flags & _PR_GCABLE_THREAD)) + _PR_MD_RESUME_THREAD(_PR_ACTIVE_THREAD_PTR(qp)); + } + _PR_MD_END_RESUME_ALL(); + suspendAllThread = NULL; + suspendAllOn = PR_FALSE; + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(PRStatus) PR_EnumerateThreads(PREnumerator func, void *arg) +{ + PRCList *qp, *qp_next; + PRIntn i = 0; + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRStatus rv = PR_SUCCESS; + PRThread* t; + + /* + ** Currently Enumerate threads happen only with suspension and + ** pr_activeLock held + */ + PR_ASSERT(suspendAllOn); + + /* Steve Morse, 4-23-97: Note that we can't walk a queue by taking + * qp->next after applying the function "func". In particular, "func" + * might remove the thread from the queue and put it into another one in + * which case qp->next no longer points to the next entry in the original + * queue. + * + * To get around this problem, we save qp->next in qp_next before applying + * "func" and use that saved value as the next value after applying "func". + */ + + /* + * Traverse the list of local and global threads + */ + for (qp = _PR_ACTIVE_LOCAL_THREADQ().next; + qp != &_PR_ACTIVE_LOCAL_THREADQ(); qp = qp_next) + { + qp_next = qp->next; + t = _PR_ACTIVE_THREAD_PTR(qp); + if (t->flags & _PR_GCABLE_THREAD) + { + rv = (*func)(t, i, arg); + if (rv != PR_SUCCESS) + return rv; + i++; + } + } + for (qp = _PR_ACTIVE_GLOBAL_THREADQ().next; + qp != &_PR_ACTIVE_GLOBAL_THREADQ(); qp = qp_next) + { + qp_next = qp->next; + t = _PR_ACTIVE_THREAD_PTR(qp); + if (t->flags & _PR_GCABLE_THREAD) + { + rv = (*func)(t, i, arg); + if (rv != PR_SUCCESS) + return rv; + i++; + } + } + return rv; +} + +/* FUNCTION: _PR_AddSleepQ +** DESCRIPTION: +** Adds a thread to the sleep/pauseQ. +** RESTRICTIONS: +** Caller must have the RUNQ lock. +** Caller must be a user level thread +*/ +PR_IMPLEMENT(void) +_PR_AddSleepQ(PRThread *thread, PRIntervalTime timeout) +{ + _PRCPU *cpu = thread->cpu; + + if (timeout == PR_INTERVAL_NO_TIMEOUT) { + /* append the thread to the global pause Q */ + PR_APPEND_LINK(&thread->links, &_PR_PAUSEQ(thread->cpu)); + thread->flags |= _PR_ON_PAUSEQ; + } else { + PRIntervalTime sleep; + PRCList *q; + PRThread *t; + + /* sort onto global sleepQ */ + sleep = timeout; + + /* Check if we are longest timeout */ + if (timeout >= _PR_SLEEPQMAX(cpu)) { + PR_INSERT_BEFORE(&thread->links, &_PR_SLEEPQ(cpu)); + thread->sleep = timeout - _PR_SLEEPQMAX(cpu); + _PR_SLEEPQMAX(cpu) = timeout; + } else { + /* Sort thread into global sleepQ at appropriate point */ + q = _PR_SLEEPQ(cpu).next; + + /* Now scan the list for where to insert this entry */ + while (q != &_PR_SLEEPQ(cpu)) { + t = _PR_THREAD_PTR(q); + if (sleep < t->sleep) { + /* Found sleeper to insert in front of */ + break; + } + sleep -= t->sleep; + q = q->next; + } + thread->sleep = sleep; + PR_INSERT_BEFORE(&thread->links, q); + + /* + ** Subtract our sleep time from the sleeper that follows us (there + ** must be one) so that they remain relative to us. + */ + PR_ASSERT (thread->links.next != &_PR_SLEEPQ(cpu)); + + t = _PR_THREAD_PTR(thread->links.next); + PR_ASSERT(_PR_THREAD_PTR(t->links.prev) == thread); + t->sleep -= sleep; + } + + thread->flags |= _PR_ON_SLEEPQ; + } +} + +/* FUNCTION: _PR_DelSleepQ +** DESCRIPTION: +** Removes a thread from the sleep/pauseQ. +** INPUTS: +** If propogate_time is true, then the thread following the deleted +** thread will be get the time from the deleted thread. This is used +** when deleting a sleeper that has not timed out. +** RESTRICTIONS: +** Caller must have the RUNQ lock. +** Caller must be a user level thread +*/ +PR_IMPLEMENT(void) +_PR_DelSleepQ(PRThread *thread, PRBool propogate_time) +{ + _PRCPU *cpu = thread->cpu; + + /* Remove from pauseQ/sleepQ */ + if (thread->flags & (_PR_ON_PAUSEQ|_PR_ON_SLEEPQ)) { + if (thread->flags & _PR_ON_SLEEPQ) { + PRCList *q = thread->links.next; + if (q != &_PR_SLEEPQ(cpu)) { + if (propogate_time == PR_TRUE) { + PRThread *after = _PR_THREAD_PTR(q); + after->sleep += thread->sleep; + } else + _PR_SLEEPQMAX(cpu) -= thread->sleep; + } else { + /* Check if prev is the beggining of the list; if so, + * we are the only element on the list. + */ + if (thread->links.prev != &_PR_SLEEPQ(cpu)) + _PR_SLEEPQMAX(cpu) -= thread->sleep; + else + _PR_SLEEPQMAX(cpu) = 0; + } + thread->flags &= ~_PR_ON_SLEEPQ; + } else { + thread->flags &= ~_PR_ON_PAUSEQ; + } + PR_REMOVE_LINK(&thread->links); + } else + PR_ASSERT(0); +} + +void +_PR_AddThreadToRunQ( + PRThread *me, /* the current thread */ + PRThread *thread) /* the local thread to be added to a run queue */ +{ + PRThreadPriority pri = thread->priority; + _PRCPU *cpu = thread->cpu; + + PR_ASSERT(!_PR_IS_NATIVE_THREAD(thread)); + +#if defined(WINNT) + /* + * On NT, we can only reliably know that the current CPU + * is not idle. We add the awakened thread to the run + * queue of its CPU if its CPU is the current CPU. + * For any other CPU, we don't really know whether it + * is busy or idle. So in all other cases, we just + * "post" the awakened thread to the IO completion port + * for the next idle CPU to execute (this is done in + * _PR_MD_WAKEUP_WAITER). + */ + if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + } +#else + _PR_RUNQ_LOCK(cpu); + _PR_ADD_RUNQ(thread, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { + if (pri > me->priority) { + _PR_SET_RESCHED_FLAG(); + } + } +#endif +} diff --git a/pr/src/threads/prcmon.c b/pr/src/threads/prcmon.c new file mode 100644 index 00000000..138e2083 --- /dev/null +++ b/pr/src/threads/prcmon.c @@ -0,0 +1,392 @@ +/* -*- 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 <stdlib.h> +#include <stddef.h> + +/* Lock used to lock the monitor cache */ +#ifdef _PR_NO_PREEMPT +#define _PR_NEW_LOCK_MCACHE() +#define _PR_LOCK_MCACHE() +#define _PR_UNLOCK_MCACHE() +#else +#ifdef _PR_LOCAL_THREADS_ONLY +#define _PR_NEW_LOCK_MCACHE() +#define _PR_LOCK_MCACHE() { PRIntn _is; _PR_INTSOFF(_is) +#define _PR_UNLOCK_MCACHE() _PR_INTSON(_is); } +#else +PRLock *_pr_mcacheLock; +#define _PR_NEW_LOCK_MCACHE() (_pr_mcacheLock = PR_NewLock()) +#define _PR_LOCK_MCACHE() PR_Lock(_pr_mcacheLock) +#define _PR_UNLOCK_MCACHE() PR_Unlock(_pr_mcacheLock) +#endif +#endif + +/************************************************************************/ + +typedef struct MonitorCacheEntryStr MonitorCacheEntry; + +struct MonitorCacheEntryStr { + MonitorCacheEntry* next; + void* address; + PRMonitor* mon; + long cacheEntryCount; +}; + +static PRUint32 hash_mask; +static PRUintn num_hash_buckets; +static PRUintn num_hash_buckets_log2; +static MonitorCacheEntry **hash_buckets; +static MonitorCacheEntry *free_entries; +static PRUintn num_free_entries; +static PRBool expanding; +int _pr_mcache_ready; + +#define HASH(address) \ + ((PRUint32) ( ((PRUptrdiff)(address) >> 2) ^ \ + ((PRUptrdiff)(address) >> 10) ) \ + & hash_mask) + +/* +** Expand the monitor cache. This grows the hash buckets and allocates a +** new chunk of cache entries and throws them on the free list. We keep +** as many hash buckets as there are entries. +** +** Because we call malloc and malloc may need the monitor cache, we must +** ensure that there are several free monitor cache entries available for +** malloc to get. FREE_THRESHOLD is used to prevent monitor cache +** starvation during monitor cache expansion. +*/ + +#define FREE_THRESHOLD 5 + +static PRStatus ExpandMonitorCache(PRUintn new_size_log2) +{ + MonitorCacheEntry **old_hash_buckets, *p; + PRUintn i, entries, old_num_hash_buckets, added; + MonitorCacheEntry **new_hash_buckets, *new_entries; + + entries = 1L << new_size_log2; + + /* + ** Expand the monitor-cache-entry free list + */ + new_entries = (MonitorCacheEntry*)PR_CALLOC( + entries * sizeof(MonitorCacheEntry)); + if (NULL == new_entries) return PR_FAILURE; + + /* + ** Allocate system monitors for the new monitor cache entries. If we + ** run out of system monitors, break out of the loop. + */ + for (i = 0, added = 0, p = new_entries; i < entries; i++, p++, added++) + { + p->mon = PR_NewMonitor(); + if (!p->mon) break; + } + if (added != entries) + { + if (added == 0) + { + /* Totally out of system monitors. Lossage abounds */ + PR_DELETE(new_entries); + return PR_FAILURE; + } + + /* + ** We were able to allocate some of the system monitors. Use + ** realloc to shrink down the new_entries memory + */ + p = (MonitorCacheEntry*)PR_REALLOC( + new_entries, added * sizeof(MonitorCacheEntry)); + if (p == 0) + { + /* + ** Total lossage. We just leaked a bunch of system monitors + ** all over the floor. This should never ever happen. + */ + PR_ASSERT(p != 0); + return PR_FAILURE; + } + } + + /* + ** Now that we have allocated all of the system monitors, build up + ** the new free list. We can just update the free_list because we own + ** the mcache-lock and we aren't calling anyone who might want to use + ** it. + */ + for (i = 0, p = new_entries; i < added - 1; i++, p++) p->next = p + 1; + p->next = free_entries; + free_entries = new_entries; + num_free_entries += added; + + /* Try to expand the hash table */ + new_hash_buckets = (MonitorCacheEntry**)PR_CALLOC( + entries * sizeof(MonitorCacheEntry*)); + if (NULL == new_hash_buckets) + { + /* + ** Partial lossage. In this situation we don't get any more hash + ** buckets, which just means that the table lookups will take + ** longer. This is bad, but not fatal + */ + PR_LOG(_pr_cmon_lm, PR_LOG_WARNING, + ("unable to grow monitor cache hash buckets")); + return PR_SUCCESS; + } + + /* + ** Compute new hash mask value. This value is used to mask an address + ** until it's bits are in the right spot for indexing into the hash + ** table. + */ + hash_mask = entries - 1; + + /* + ** Expand the hash table. We have to rehash everything in the old + ** table into the new table. + */ + old_hash_buckets = hash_buckets; + old_num_hash_buckets = num_hash_buckets; + for (i = 0; i < old_num_hash_buckets; i++) + { + p = old_hash_buckets[i]; + while (p) + { + MonitorCacheEntry *next = p->next; + + /* Hash based on new table size, and then put p in the new table */ + PRUintn hash = HASH(p->address); + p->next = new_hash_buckets[hash]; + new_hash_buckets[hash] = p; + + p = next; + } + } + + /* + ** Switch over to new hash table and THEN call free of the old + ** table. Since free might re-enter _pr_mcache_lock, things would + ** break terribly if it used the old hash table. + */ + hash_buckets = new_hash_buckets; + num_hash_buckets = entries; + num_hash_buckets_log2 = new_size_log2; + PR_DELETE(old_hash_buckets); + + PR_LOG(_pr_cmon_lm, PR_LOG_NOTICE, + ("expanded monitor cache to %d (buckets %d)", + num_free_entries, entries)); + + return PR_SUCCESS; +} /* ExpandMonitorCache */ + +/* +** Lookup a monitor cache entry by address. Return a pointer to the +** pointer to the monitor cache entry on success, null on failure. +*/ +static MonitorCacheEntry **LookupMonitorCacheEntry(void *address) +{ + PRUintn hash; + MonitorCacheEntry **pp, *p; + + hash = HASH(address); + pp = hash_buckets + hash; + while ((p = *pp) != 0) { + if (p->address == address) { + if (p->cacheEntryCount > 0) + return pp; + else + return NULL; + } + pp = &p->next; + } + return NULL; +} + +/* +** Try to create a new cached monitor. If it's already in the cache, +** great - return it. Otherwise get a new free cache entry and set it +** up. If the cache free space is getting low, expand the cache. +*/ +static PRMonitor *CreateMonitor(void *address) +{ + PRUintn hash; + MonitorCacheEntry **pp, *p; + + hash = HASH(address); + pp = hash_buckets + hash; + while ((p = *pp) != 0) { + if (p->address == address) goto gotit; + + pp = &p->next; + } + + /* Expand the monitor cache if we have run out of free slots in the table */ + if (num_free_entries < FREE_THRESHOLD) + { + /* Expand monitor cache */ + + /* + ** This function is called with the lock held. So what's the 'expanding' + ** boolean all about? Seems a bit redundant. + */ + if (!expanding) + { + PRStatus rv; + + expanding = PR_TRUE; + rv = ExpandMonitorCache(num_hash_buckets_log2 + 1); + expanding = PR_FALSE; + if (PR_FAILURE == rv) return NULL; + + /* redo the hash because it'll be different now */ + hash = HASH(address); + } + else + { + /* + ** We are in process of expanding and we need a cache + ** monitor. Make sure we have enough! + */ + PR_ASSERT(num_free_entries > 0); + } + } + + /* Make a new monitor */ + p = free_entries; + free_entries = p->next; + num_free_entries--; + p->address = address; + p->next = hash_buckets[hash]; + hash_buckets[hash] = p; + PR_ASSERT(p->cacheEntryCount == 0); + + gotit: + p->cacheEntryCount++; + return p->mon; +} + +/* +** Initialize the monitor cache +*/ +void _PR_InitCMon(void) +{ + _PR_NEW_LOCK_MCACHE(); + ExpandMonitorCache(3); + _pr_mcache_ready = 1; +} + +/* +** Create monitor for address. Don't enter the monitor while we have the +** mcache locked because we might block! +*/ +PR_IMPLEMENT(PRMonitor*) PR_CEnterMonitor(void *address) +{ + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + mon = CreateMonitor(address); + _PR_UNLOCK_MCACHE(); + + if (!mon) return NULL; + + PR_EnterMonitor(mon); + return mon; +} + +PR_IMPLEMENT(PRStatus) PR_CExitMonitor(void *address) +{ + MonitorCacheEntry **pp, *p; + PRStatus status = PR_SUCCESS; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + if (pp != NULL) { + p = *pp; + if (--p->cacheEntryCount == 0) { + /* + ** Nobody is using the system monitor. Put it on the cached free + ** list. We are safe from somebody trying to use it because we + ** have the mcache locked. + */ + p->address = 0; /* defensive move */ + *pp = p->next; /* unlink from hash_buckets */ + p->next = free_entries; /* link into free list */ + free_entries = p; + num_free_entries++; /* count it as free */ + } + status = PR_ExitMonitor(p->mon); + } else { + status = PR_FAILURE; + } + _PR_UNLOCK_MCACHE(); + + return status; +} + +PR_IMPLEMENT(PRStatus) PR_CWait(void *address, PRIntervalTime ticks) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) + return PR_FAILURE; + else + return PR_Wait(mon, ticks); +} + +PR_IMPLEMENT(PRStatus) PR_CNotify(void *address) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) + return PR_FAILURE; + else + return PR_Notify(mon); +} + +PR_IMPLEMENT(PRStatus) PR_CNotifyAll(void *address) +{ + MonitorCacheEntry **pp; + PRMonitor *mon; + + _PR_LOCK_MCACHE(); + pp = LookupMonitorCacheEntry(address); + mon = pp ? ((*pp)->mon) : NULL; + _PR_UNLOCK_MCACHE(); + + if (mon == NULL) + return PR_FAILURE; + else + return PR_NotifyAll(mon); +} diff --git a/pr/src/threads/prcthr.c b/pr/src/threads/prcthr.c new file mode 100644 index 00000000..cf87e22b --- /dev/null +++ b/pr/src/threads/prcthr.c @@ -0,0 +1,414 @@ +/* -*- 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" + +/* +** Routines common to both native and user threads. +** +** +** Clean up a thread object, releasing all of the attached data. Do not +** free the object itself (it may not have been malloc'd) +*/ +void _PR_CleanupThread(PRThread *thread) +{ + PRUintn i; + void **ptd; + _PRPerThreadExit *pte; + PRThreadPrivateDTOR *destructor; + + /* Free up per-thread-data */ + ptd = thread->privateData; + destructor = _pr_tpd_destructors; + for (i = 0; i < thread->tpdLength; i++, ptd++, destructor++) + { + if (*destructor && *ptd) (**destructor)(*ptd); + *ptd = NULL; + } + + /* Free any thread dump procs */ + if (thread->dumpArg) { + PR_DELETE(thread->dumpArg); + } + thread->dump = 0; + + /* Invoke per-thread exit functions */ + pte = &thread->ptes[0]; + for (i = 0; i < thread->numExits; i++, pte++) { + if (pte->func) { + (*pte->func)(pte->arg); + pte->func = 0; + } + } + if (thread->ptes) { + PR_DELETE(thread->ptes); + thread->numExits = 0; + } + PR_ASSERT(thread->numExits == 0); + PR_DELETE(thread->errorString); + thread->errorStringSize = 0; + thread->environment = NULL; +} + +PR_IMPLEMENT(PRStatus) PR_Yield() +{ + static PRBool warning = PR_TRUE; + if (warning) warning = _PR_Obsolete( + "PR_Yield()", "PR_Sleep(PR_INTERVAL_NO_WAIT)"); + return (PR_Sleep(PR_INTERVAL_NO_WAIT)); +} + +/* +** Make the current thread sleep until "timeout" ticks amount of time +** has expired. If "timeout" is PR_INTERVAL_NO_WAIT then the call is +** equivalent to a yield. Waiting for an infinite amount of time is +** allowed in the expectation that another thread will interrupt(). +** +** A single lock is used for all threads calling sleep. Each caller +** does get its own condition variable since each is expected to have +** a unique 'timeout'. +*/ +PR_IMPLEMENT(PRStatus) PR_Sleep(PRIntervalTime timeout) +{ + static PRLock *ml = NULL; + PRStatus rv = PR_SUCCESS; + if (PR_INTERVAL_NO_WAIT == timeout) + { + /* + ** This is a simple yield, nothing more, nothing less. + */ + PRIntn is; + PRThread *me = PR_GetCurrentThread(); + PRUintn pri = me->priority; + _PRCPU *cpu = _PR_MD_CURRENT_CPU(); + + if ( _PR_IS_NATIVE_THREAD(me) ) _PR_MD_YIELD(); + else + { + _PR_INTSOFF(is); + _PR_RUNQ_LOCK(cpu); + if (_PR_RUNQREADYMASK(cpu) >> pri) { + me->cpu = cpu; + me->state = _PR_RUNNABLE; + _PR_ADD_RUNQ(me, cpu, pri); + _PR_RUNQ_UNLOCK(cpu); + + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: yielding")); + _PR_MD_SWITCH_CONTEXT(me); + PR_LOG(_pr_sched_lm, PR_LOG_MIN, ("PR_Yield: done")); + + _PR_FAST_INTSON(is); + } + else + { + _PR_RUNQ_UNLOCK(cpu); + _PR_INTSON(is); + } + } + } + else + { + /* + ** This is waiting for some finite period of time. + ** A thread in this state is interruptible (PR_Interrupt()), + ** but the lock and cvar used are local to the implementation + ** and not visible to the caller, therefore not notifiable. + */ + if (ml == NULL) ml = PR_NewLock(); + + if (ml == NULL) rv = PR_FAILURE; + else + { + PRCondVar *cv = PR_NewCondVar(ml); + PRIntervalTime timein = PR_IntervalNow(); + + PR_Lock(ml); + while (rv == PR_SUCCESS) + { + PRIntervalTime delta = PR_IntervalNow() - timein; + if (delta > timeout) break; + rv = PR_WaitCondVar(cv, timeout - delta); + } + PR_Unlock(ml); + + PR_DestroyCondVar(cv); + } + } + return rv; +} + +PR_IMPLEMENT(PRUint32) PR_GetThreadID(PRThread *thread) +{ + return thread->id; +} + +PR_IMPLEMENT(PRThreadPriority) PR_GetThreadPriority(const PRThread *thread) +{ + return (PRThreadPriority) thread->priority; +} + +PR_IMPLEMENT(PRThread *) PR_GetCurrentThread() +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return _PR_MD_CURRENT_THREAD(); +} + +/* +** Set the interrupt flag for a thread. The thread will be unable to +** block in i/o functions when this happens. Also, any PR_Wait's in +** progress will be undone. The interrupt remains in force until +** PR_ClearInterrupt is called. +*/ +PR_IMPLEMENT(PRStatus) PR_Interrupt(PRThread *thread) +{ +#ifdef _PR_GLOBAL_THREADS_ONLY + PRCondVar *victim; + + _PR_THREAD_LOCK(thread); + thread->flags |= _PR_INTERRUPT; + victim = thread->wait.cvar; + _PR_THREAD_UNLOCK(thread); + if (NULL != victim) { + int haveLock = (victim->lock->owner == _PR_MD_CURRENT_THREAD()); + + if (!haveLock) PR_Lock(victim->lock); + PR_NotifyAllCondVar(victim); + if (!haveLock) PR_Unlock(victim->lock); + } + return PR_SUCCESS; +#else /* ! _PR_GLOBAL_THREADS_ONLY */ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSOFF(is); + + _PR_THREAD_LOCK(thread); + thread->flags |= _PR_INTERRUPT; + switch (thread->state) { + case _PR_COND_WAIT: + /* + * call is made with thread locked; + * on return lock is released + */ + _PR_NotifyLockedThread(thread); + break; + case _PR_IO_WAIT: + /* + * Need to hold the thread lock when calling + * _PR_Unblock_IO_Wait(). On return lock is + * released. + */ +#if defined(XP_UNIX) || defined(WINNT) || defined(WIN16) + _PR_Unblock_IO_Wait(thread); +#else + _PR_THREAD_UNLOCK(thread); +#endif + break; + case _PR_RUNNING: + case _PR_RUNNABLE: + case _PR_LOCK_WAIT: + default: + _PR_THREAD_UNLOCK(thread); + break; + } + if (!_PR_IS_NATIVE_THREAD(me)) + _PR_INTSON(is); + return PR_SUCCESS; +#endif /* _PR_GLOBAL_THREADS_ONLY */ +} + +/* +** Clear the interrupt flag for self. +*/ +PR_IMPLEMENT(void) PR_ClearInterrupt() +{ + PRIntn is; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); + _PR_THREAD_LOCK(me); + me->flags &= ~_PR_INTERRUPT; + _PR_THREAD_UNLOCK(me); + if ( !_PR_IS_NATIVE_THREAD(me)) _PR_INTSON(is); +} + +/* +** Return the thread stack pointer of the given thread. +*/ +PR_IMPLEMENT(void *) PR_GetSP(PRThread *thread) +{ + return (void *)_PR_MD_GET_SP(thread); +} + +PR_IMPLEMENT(void*) GetExecutionEnvironment(PRThread *thread) +{ + return thread->environment; +} + +PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *env) +{ + thread->environment = env; +} + + +PR_IMPLEMENT(PRInt32) PR_GetThreadAffinityMask(PRThread *thread, PRUint32 *mask) +{ +#ifdef HAVE_THREAD_AFFINITY +/* + * Irix ignores the thread argument + */ +#ifndef IRIX + if (_PR_IS_NATIVE_THREAD(thread)) + return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); + else + return 0; +#else + return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); +#endif /* !IRIX */ +#else + +#if defined(XP_MAC) +#pragma unused (thread, mask) +#endif + + return 0; +#endif +} + +PR_IMPLEMENT(PRInt32) PR_SetThreadAffinityMask(PRThread *thread, PRUint32 mask ) +{ +#ifdef HAVE_THREAD_AFFINITY +#ifndef IRIX + return _PR_MD_SETTHREADAFFINITYMASK(thread, mask); +#endif +#else + +#if defined(XP_MAC) +#pragma unused (thread, mask) +#endif + + return 0; +#endif +} + +/* This call is thread unsafe if another thread is calling SetConcurrency() + */ +PR_IMPLEMENT(PRInt32) PR_SetCPUAffinityMask(PRUint32 mask) +{ +#ifdef HAVE_THREAD_AFFINITY + PRCList *qp; + extern PRUint32 _pr_cpu_affinity_mask; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + _pr_cpu_affinity_mask = mask; + + qp = _PR_CPUQ().next; + while(qp != &_PR_CPUQ()) { + _PRCPU *cpu; + + cpu = _PR_CPU_PTR(qp); + PR_SetThreadAffinityMask(cpu->thread, mask); + + qp = qp->next; + } +#endif + +#if defined(XP_MAC) +#pragma unused (mask) +#endif + + return 0; +} + +PRUint32 _pr_recycleThreads = 0; +PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 count) +{ + _pr_recycleThreads = count; +} + +PR_IMPLEMENT(PRThread*) PR_CreateThreadGCAble(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRThreadPriority priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, _PR_GCABLE_THREAD); +} + +#ifdef SOLARIS +PR_IMPLEMENT(PRThread*) PR_CreateThreadBound(PRThreadType type, + void (*start)(void *arg), + void *arg, + PRUintn priority, + PRThreadScope scope, + PRThreadState state, + PRUint32 stackSize) +{ + return _PR_CreateThread(type, start, arg, priority, scope, state, + stackSize, _PR_BOUND_THREAD); +} +#endif + + +PR_IMPLEMENT(PRThread*) PR_AttachThreadGCAble( + PRThreadType type, PRThreadPriority priority, PRThreadStack *stack) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return _PRI_AttachThread(type, priority, stack, _PR_GCABLE_THREAD); +} + +PR_IMPLEMENT(void) PR_SetThreadGCAble() +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + PR_Lock(_pr_activeLock); + _PR_MD_CURRENT_THREAD()->flags |= _PR_GCABLE_THREAD; + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(void) PR_ClearThreadGCAble() +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + PR_Lock(_pr_activeLock); + _PR_MD_CURRENT_THREAD()->flags &= (~_PR_GCABLE_THREAD); + PR_Unlock(_pr_activeLock); +} + +PR_IMPLEMENT(PRThreadScope) PR_GetThreadScope(const PRThread *thread) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (_PR_IS_NATIVE_THREAD(thread)) + return PR_GLOBAL_THREAD; + else + return PR_LOCAL_THREAD; +} + +PR_IMPLEMENT(PRThreadType) PR_GetThreadType(const PRThread *thread) +{ + return (thread->flags & _PR_SYSTEM) ? PR_SYSTEM_THREAD : PR_USER_THREAD; +} + +PR_IMPLEMENT(PRThreadState) PR_GetThreadState(const PRThread *thread) +{ + return (NULL == thread->term) ? PR_UNJOINABLE_THREAD : PR_JOINABLE_THREAD; +} /* PR_GetThreadState */ diff --git a/pr/src/threads/prdump.c b/pr/src/threads/prdump.c new file mode 100644 index 00000000..471a0dfe --- /dev/null +++ b/pr/src/threads/prdump.c @@ -0,0 +1,124 @@ +/* -*- 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" + +/* XXX use unbuffered nspr stdio */ + +PRFileDesc *_pr_dumpOut; + +PRUint32 _PR_DumpPrintf(PRFileDesc *fd, const char *fmt, ...) +{ + char buf[100]; + PRUint32 nb; + va_list ap; + + va_start(ap, fmt); + nb = PR_vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + PR_Write(fd, buf, nb); + + return nb; +} + +void _PR_DumpThread(PRFileDesc *fd, PRThread *thread) +{ + +#ifndef _PR_GLOBAL_THREADS_ONLY + _PR_DumpPrintf(fd, "%05d[%08p] pri=%2d flags=0x%02x", + thread->id, thread, thread->priority, thread->flags); + switch (thread->state) { + case _PR_RUNNABLE: + case _PR_RUNNING: + break; + case _PR_LOCK_WAIT: + _PR_DumpPrintf(fd, " lock=%p", thread->wait.lock); + break; + case _PR_COND_WAIT: + _PR_DumpPrintf(fd, " condvar=%p sleep=%lldms", + thread->wait.cvar, thread->sleep); + break; + case _PR_SUSPENDED: + _PR_DumpPrintf(fd, " suspended"); + break; + } + PR_Write(fd, "\n", 1); +#endif + + /* Now call dump routine */ + if (thread->dump) { + thread->dump(fd, thread, thread->dumpArg); + } +} + +static void DumpThreadQueue(PRFileDesc *fd, PRCList *list) +{ +#ifndef _PR_GLOBAL_THREADS_ONLY + PRCList *q; + + q = list->next; + while (q != list) { + PRThread *t = _PR_THREAD_PTR(q); + _PR_DumpThread(fd, t); + q = q->next; + } +#endif +} + +void _PR_DumpThreads(PRFileDesc *fd) +{ + PRThread *t; + PRIntn i; + + _PR_DumpPrintf(fd, "Current Thread:\n"); + t = _PR_MD_CURRENT_THREAD(); + _PR_DumpThread(fd, t); + + _PR_DumpPrintf(fd, "Runnable Threads:\n"); + for (i = 0; i < 32; i++) { + DumpThreadQueue(fd, &_PR_RUNQ(t->cpu)[i]); + } + + _PR_DumpPrintf(fd, "CondVar timed wait Threads:\n"); + DumpThreadQueue(fd, &_PR_SLEEPQ(t->cpu)); + + _PR_DumpPrintf(fd, "CondVar wait Threads:\n"); + DumpThreadQueue(fd, &_PR_PAUSEQ(t->cpu)); + + _PR_DumpPrintf(fd, "Suspended Threads:\n"); + DumpThreadQueue(fd, &_PR_SUSPENDQ(t->cpu)); +} + +void PR_ShowStatus(void) +{ + PRIntn is; + + if ( _PR_MD_CURRENT_THREAD() + && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_INTSOFF(is); + _pr_dumpOut = _pr_stderr; + _PR_DumpThreads(_pr_dumpOut); + if ( _PR_MD_CURRENT_THREAD() + && !_PR_IS_NATIVE_THREAD(_PR_MD_CURRENT_THREAD())) _PR_FAST_INTSON(is); +} + +PR_IMPLEMENT(void) +PR_SetThreadDumpProc(PRThread* thread, PRThreadDumpProc dump, void *arg) +{ + thread->dump = dump; + thread->dumpArg = arg; +} diff --git a/pr/src/threads/prmon.c b/pr/src/threads/prmon.c new file mode 100644 index 00000000..d6b708c1 --- /dev/null +++ b/pr/src/threads/prmon.c @@ -0,0 +1,202 @@ +/* -*- 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" + +/************************************************************************/ + +/* +** Create a new monitor. +*/ +PR_IMPLEMENT(PRMonitor*) PR_NewMonitor() +{ + PRMonitor *mon; + PRCondVar *cvar; + PRLock *lock; + + mon = PR_NEWZAP(PRMonitor); + if (mon) { + lock = PR_NewLock(); + if (!lock) { + PR_DELETE(mon); + return 0; + } + + cvar = PR_NewCondVar(lock); + if (!cvar) { + PR_DestroyLock(lock); + PR_DELETE(mon); + return 0; + } + mon->cvar = cvar; + mon->name = NULL; + } + return mon; +} + +PR_IMPLEMENT(PRMonitor*) PR_NewNamedMonitor(const char* name) +{ + PRMonitor* mon = PR_NewMonitor(); + mon->name = name; + return mon; +} + +/* +** Destroy a monitor. There must be no thread waiting on the monitor's +** condition variable. The caller is responsible for guaranteeing that the +** monitor is no longer in use. +*/ +PR_IMPLEMENT(void) PR_DestroyMonitor(PRMonitor *mon) +{ + PR_DestroyLock(mon->cvar->lock); + PR_DestroyCondVar(mon->cvar); + PR_DELETE(mon); +} + +/* +** Enter the lock associated with the monitor. +*/ +PR_IMPLEMENT(void) PR_EnterMonitor(PRMonitor *mon) +{ + if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) { + mon->entryCount++; + } else { + PR_Lock(mon->cvar->lock); + mon->entryCount = 1; + } +} + +/* +** Test and then enter the lock associated with the monitor if it's not +** already entered by some other thread. Return PR_FALSE if some other +** thread owned the lock at the time of the call. +*/ +PR_IMPLEMENT(PRBool) PR_TestAndEnterMonitor(PRMonitor *mon) +{ + if (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) { + mon->entryCount++; + return PR_TRUE; + } else { + if (PR_TestAndLock(mon->cvar->lock)) { + mon->entryCount = 1; + return PR_TRUE; + } + } + return PR_FALSE; +} + +/* +** Exit the lock associated with the monitor once. +*/ +PR_IMPLEMENT(PRStatus) PR_ExitMonitor(PRMonitor *mon) +{ + if (mon->cvar->lock->owner != _PR_MD_CURRENT_THREAD()) { + return PR_FAILURE; + } + if (--mon->entryCount == 0) { + return PR_Unlock(mon->cvar->lock); + } + return PR_SUCCESS; +} + +/* +** Return the number of times that the current thread has entered the +** lock. Returns zero if the current thread has not entered the lock. +*/ +PR_IMPLEMENT(PRIntn) PR_GetMonitorEntryCount(PRMonitor *mon) +{ + return (mon->cvar->lock->owner == _PR_MD_CURRENT_THREAD()) ? + mon->entryCount : 0; +} + +/* +** Wait for a notify on the condition variable. Sleep for "ticks" amount +** of time (if "tick" is 0 then the sleep is indefinite). While +** the thread is waiting it exits the monitors lock (as if it called +** PR_ExitMonitor as many times as it had called PR_EnterMonitor). When +** the wait has finished the thread regains control of the monitors lock +** with the same entry count as before the wait began. +** +** The thread waiting on the monitor will be resumed when the monitor is +** notified (assuming the thread is the next in line to receive the +** notify) or when the "ticks" elapses. +** +** Returns PR_FAILURE if the caller has not locked the lock associated +** with the condition variable. +** This routine can return PR_PENDING_INTERRUPT if the waiting thread +** has been interrupted. +*/ +PR_IMPLEMENT(PRStatus) PR_Wait(PRMonitor *mon, PRIntervalTime ticks) +{ + PRUintn entryCount; + PRStatus status; + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (mon->cvar->lock->owner != me) return PR_FAILURE; + + entryCount = mon->entryCount; + mon->entryCount = 0; + + status = _PR_WaitCondVar(me, mon->cvar, mon->cvar->lock, ticks); + + mon->entryCount = entryCount; + + return status; +} + +/* +** Notify the highest priority thread waiting on the condition +** variable. If a thread is waiting on the condition variable (using +** PR_Wait) then it is awakened and begins waiting on the monitor's lock. +*/ +PR_IMPLEMENT(PRStatus) PR_Notify(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + if (mon->cvar->lock->owner != me) return PR_FAILURE; + PR_NotifyCondVar(mon->cvar); + return PR_SUCCESS; +} + +/* +** Notify all of the threads waiting on the condition variable. All of +** threads are notified in turn. The highest priority thread will +** probably acquire the monitor first when the monitor is exited. +*/ +PR_IMPLEMENT(PRStatus) PR_NotifyAll(PRMonitor *mon) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + if (mon->cvar->lock->owner != me) return PR_FAILURE; + PR_NotifyAllCondVar(mon->cvar); + return PR_SUCCESS; +} + +/************************************************************************/ + +PRUint32 _PR_MonitorToString(PRMonitor *mon, char *buf, PRUint32 buflen) +{ + PRUint32 nb; + + if (mon->cvar->lock->owner) { + nb = PR_snprintf(buf, buflen, "[%p] owner=%d[%p] count=%ld", + mon, mon->cvar->lock->owner->id, + mon->cvar->lock->owner, mon->entryCount); + } else { + nb = PR_snprintf(buf, buflen, "[%p]", mon); + } + return nb; +} diff --git a/pr/src/threads/prsem.c b/pr/src/threads/prsem.c new file mode 100644 index 00000000..62695e78 --- /dev/null +++ b/pr/src/threads/prsem.c @@ -0,0 +1,155 @@ +/* -*- 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" +#if defined(XP_MAC) +#include "prsem.h" +#else +#include "obsolete/prsem.h" +#endif + +/************************************************************************/ + +/* +** Create a new semaphore. +*/ +PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) +{ + PRSemaphore *sem; + PRCondVar *cvar; + PRLock *lock; + + sem = PR_NEWZAP(PRSemaphore); + if (sem) { +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_NEW_SEM(&sem->md, value); +#else + lock = PR_NewLock(); + if (!lock) { + PR_DELETE(sem); + return NULL; + } + + cvar = PR_NewCondVar(lock); + if (!cvar) { + PR_DestroyLock(lock); + PR_DELETE(sem); + return NULL; + } + sem->cvar = cvar; + sem->count = value; +#endif + } + return sem; +} + +/* +** Destroy a semaphore. There must be no thread waiting on the semaphore. +** The caller is responsible for guaranteeing that the semaphore is +** no longer in use. +*/ +PR_IMPLEMENT(void) PR_DestroySem(PRSemaphore *sem) +{ +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_DESTROY_SEM(&sem->md); +#else + PR_ASSERT(sem->waiters == 0); + + PR_DestroyLock(sem->cvar->lock); + PR_DestroyCondVar(sem->cvar); +#endif + PR_DELETE(sem); +} + +/* +** Wait on a Semaphore. +** +** This routine allows a calling thread to wait or proceed depending upon the +** state of the semahore sem. The thread can proceed only if the counter value +** of the semaphore sem is currently greater than 0. If the value of semaphore +** sem is positive, it is decremented by one and the routine returns immediately +** allowing the calling thread to continue. If the value of semaphore sem is 0, +** the calling thread blocks awaiting the semaphore to be released by another +** thread. +** +** This routine can return PR_PENDING_INTERRUPT if the waiting thread +** has been interrupted. +*/ +PR_IMPLEMENT(PRStatus) PR_WaitSem(PRSemaphore *sem) +{ + PRStatus status = PR_SUCCESS; + +#ifdef HAVE_CVAR_BUILT_ON_SEM + return _PR_MD_WAIT_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + while (sem->count == 0) { + sem->waiters++; + status = PR_WaitCondVar(sem->cvar, PR_INTERVAL_NO_TIMEOUT); + sem->waiters--; + if (status != PR_SUCCESS) + break; + } + if (status == PR_SUCCESS) + sem->count--; + PR_Unlock(sem->cvar->lock); +#endif + + return (status); +} + +/* +** This routine increments the counter value of the semaphore. If other threads +** are blocked for the semaphore, then the scheduler will determine which ONE +** thread will be unblocked. +*/ +PR_IMPLEMENT(void) PR_PostSem(PRSemaphore *sem) +{ +#ifdef HAVE_CVAR_BUILT_ON_SEM + _PR_MD_POST_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + if (sem->waiters) + PR_NotifyCondVar(sem->cvar); + sem->count++; + PR_Unlock(sem->cvar->lock); +#endif +} + +#if DEBUG +/* +** Returns the value of the semaphore referenced by sem without affecting +** the state of the semaphore. The value represents the semaphore vaule +** at the time of the call, but may not be the actual value when the +** caller inspects it. (FOR DEBUGGING ONLY) +*/ +PR_IMPLEMENT(PRUintn) PR_GetValueSem(PRSemaphore *sem) +{ + PRUintn rv; + +#ifdef HAVE_CVAR_BUILT_ON_SEM + rv = _PR_MD_GET_VALUE_SEM(&sem->md); +#else + PR_Lock(sem->cvar->lock); + rv = sem->count; + PR_Unlock(sem->cvar->lock); +#endif + + return rv; +} +#endif diff --git a/pr/src/threads/prtpd.c b/pr/src/threads/prtpd.c new file mode 100644 index 00000000..b92ebbbb --- /dev/null +++ b/pr/src/threads/prtpd.c @@ -0,0 +1,296 @@ +/* -*- 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 <string.h> + +/* +** Thread Private Data +** +** There is an aribitrary limit on the number of keys that will be allocated +** by the runtime. It's largish, so it is intended to be a sanity check, not +** an impediment. +** +** There is a counter, initialized to zero and incremented every time a +** client asks for a new key, that holds the high water mark for keys. All +** threads logically have the same high water mark and are permitted to +** ask for TPD up to that key value. +** +** The vector to hold the TPD are allocated when PR_SetThreadPrivate() is +** called. The size of the vector will be some value greater than or equal +** to the current high water mark. Each thread has its own TPD length and +** vector. +** +** Threads that get private data for keys they have not set (or perhaps +** don't even exist for that thread) get a NULL return. If the key is +** beyond the high water mark, an error will be returned. +*/ + +#define _PR_TPD_MODULO 8 /* vectors are extended by this much */ +#define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ +static PRUintn _pr_tpd_highwater = 0; /* next TPD key to be assigned */ +static PRUintn _pr_tpd_length = 0; /* current length of destructor vector */ +PRThreadPrivateDTOR *_pr_tpd_destructors = NULL; + /* the destructors are associated with + the keys, therefore asserting that + the TPD key depicts the data's 'type' */ + +/* Lock protecting the index assignment of per-thread-private data table */ +#ifdef _PR_NO_PREEMPT +#define _PR_NEW_LOCK_TPINDEX() +#define _PR_FREE_LOCK_TPINDEX() +#define _PR_LOCK_TPINDEX() +#define _PR_UNLOCK_TPINDEX() +#else +#ifdef _PR_LOCAL_THREADS_ONLY +#define _PR_NEW_LOCK_TPINDEX() +#define _PR_FREE_LOCK_TPINDEX() +#define _PR_LOCK_TPINDEX() _PR_INTSOFF(_is) +#define _PR_UNLOCK_TPINDEX() _PR_INTSON(_is) +#else +static PRLock *_pr_threadPrivateIndexLock; +#define _PR_NEW_LOCK_TPINDEX() (_pr_threadPrivateIndexLock = PR_NewLock()) +#define _PR_FREE_LOCK_TPINDEX() (PR_DestroyLock(_pr_threadPrivateIndexLock)) +#define _PR_LOCK_TPINDEX() PR_Lock(_pr_threadPrivateIndexLock) +#define _PR_UNLOCK_TPINDEX() PR_Unlock(_pr_threadPrivateIndexLock) +#endif +#endif + +/* +** Initialize the thread private data manipulation +*/ +void _PR_InitTPD() +{ + _PR_NEW_LOCK_TPINDEX(); +} + +/* +** Clean up the thread private data manipulation +*/ +void _PR_CleanupTPD(void) +{ + _PR_FREE_LOCK_TPINDEX(); +} + +/* +** This routine returns a new index for per-thread-private data table. +** The index is visible to all threads within a process. This index can +** be used with the PR_SetThreadPrivate() and PR_GetThreadPrivate() routines +** to save and retrieve data associated with the index for a thread. +** +** The index independently maintains specific values for each binding thread. +** A thread can only get access to its own thread-specific-data. +** +** Upon a new index return the value associated with the index for all threads +** is NULL, and upon thread creation the value associated with all indices for +** that thread is NULL. +** +** "dtor" is the destructor function to invoke when the private +** data is destroyed +** +** Returns PR_FAILURE if the total number of indices will exceed the maximun +** allowed. +*/ + +PR_IMPLEMENT(PRStatus) PR_NewThreadPrivateIndex( + PRUintn *newIndex, PRThreadPrivateDTOR dtor) +{ + PRStatus rv; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + + + if (_pr_tpd_highwater >= _PR_TPD_LIMIT) + { + PR_SetError(PR_TPD_RANGE_ERROR, 0); + rv = PR_FAILURE; /* that's just wrong */ + } + else + { + PRThreadPrivateDTOR *old = NULL; + PRIntn _is; + + _PR_LOCK_TPINDEX(); + if (_pr_tpd_highwater >= _pr_tpd_length) + { + old = _pr_tpd_destructors; + _pr_tpd_destructors = PR_CALLOC( + (_pr_tpd_length + _PR_TPD_MODULO) * sizeof(PRThreadPrivateDTOR*)); + if (NULL == _pr_tpd_destructors) + { + _pr_tpd_destructors = old; old = NULL; + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + rv = PR_FAILURE; /* that's just wrong */ + goto failed; /* then extract one's self */ + } + else + { + memcpy( + _pr_tpd_destructors, old, + _pr_tpd_length * sizeof(PRThreadPrivateDTOR*)); + _pr_tpd_length += _PR_TPD_MODULO; + } + } + + *newIndex = _pr_tpd_highwater++; /* this is really all we wanted */ + _pr_tpd_destructors[*newIndex] = dtor; /* record destructor @index */ + +failed: + _PR_UNLOCK_TPINDEX(); + if (NULL != old) PR_DELETE(old); + rv = PR_SUCCESS; + } + + return rv; +} + +/* +** Define some per-thread-private data. +** "index" is an index into the per-thread private data table +** "priv" is the per-thread-private data +** +** If the per-thread private data table has a previously registered +** destructor function and a non-NULL per-thread-private data value, +** the destructor function is invoked. +** +** This can return PR_FAILURE if index is invalid (ie., beyond the current +** high water mark) or memory is insufficient to allocate an exanded vector. +*/ + +PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn index, void *priv) +{ + PRStatus rv = PR_SUCCESS; + PRThread *self = PR_GetCurrentThread(); + + /* + ** The index being set might not have a sufficient vector in this + ** thread. But if the index has been allocated, it's okay to go + ** ahead and extend this one now. + */ + if (index >= _pr_tpd_highwater) + { + PR_ASSERT(index < _pr_tpd_highwater); + PR_SetError(PR_TPD_RANGE_ERROR, 0); + rv = PR_FAILURE; + } + else + { + PRIntn _is; + + _PR_LOCK_TPINDEX(); + if ((NULL == self->privateData) || (self->tpdLength <= index)) + { + void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); + PR_ASSERT( + ((NULL == self->privateData) && (0 == self->tpdLength)) + || ((NULL != self->privateData) && (0 != self->tpdLength))); + if (NULL != extension) + { + (void)memcpy( + extension, self->privateData, + self->tpdLength * sizeof(void*)); + self->tpdLength = _pr_tpd_length; + self->privateData = extension; + } + } + /* + ** There wasn't much chance of having to call the destructor + ** unless the slot already existed. + */ + else if (self->privateData[index] && _pr_tpd_destructors[index]) + { + void *data = self->privateData[index]; + self->privateData[index] = NULL; + _PR_UNLOCK_TPINDEX(); + (*_pr_tpd_destructors[index])(data); + _PR_LOCK_TPINDEX(); + } + + /* + ** If the thread's private data is still NULL, then we couldn't + ** fix the problem. We must be outa-memory (again). + */ + if (NULL == self->privateData) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + rv = PR_FAILURE; + } + else self->privateData[index] = priv; + _PR_UNLOCK_TPINDEX(); + } + + return rv; +} + +/* +** Recover the per-thread-private data for the current thread. "index" is +** the index into the per-thread private data table. +** +** The returned value may be NULL which is indistinguishable from an error +** condition. +** +*/ + +PR_IMPLEMENT(void*) PR_GetThreadPrivate(uintn index) +{ + PRThread *self = PR_GetCurrentThread(); + void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? + NULL : self->privateData[index]; + + return tpd; +} + +PR_IMPLEMENT(PRStatus) PR_SetThreadExit(PRUintn index, PRThreadExit func, void *arg) +{ + _PRPerThreadExit *pte; + PRThread *thread = _PR_MD_CURRENT_THREAD(); + + if (index >= thread->numExits) { + if (thread->ptes) { + thread->ptes = (_PRPerThreadExit*) + PR_REALLOC(thread->ptes, (index+1) * sizeof(_PRPerThreadExit)); + } else { + thread->ptes = (_PRPerThreadExit*) + PR_CALLOC(index+1 * sizeof(_PRPerThreadExit)); + } + if (!thread->ptes) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return PR_FAILURE; + } + thread->numExits = index + 1; + } + pte = &thread->ptes[index]; + pte->func = func; + pte->arg = arg; + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRThreadExit) PR_GetThreadExit(PRUintn index, void **argp) +{ + _PRPerThreadExit *pte; + PRThread *thread = _PR_MD_CURRENT_THREAD(); + + if (index >= thread->numExits) { + if (argp) *argp = 0; + return 0; + } + pte = &thread->ptes[index]; + if (argp) *argp = pte->arg; + return pte->func; +} |