diff options
author | larryh%netscape.com <devnull@localhost> | 1999-10-13 18:32:42 +0000 |
---|---|---|
committer | larryh%netscape.com <devnull@localhost> | 1999-10-13 18:32:42 +0000 |
commit | 37bfd11ac779147a379619221da90fe589b8ac44 (patch) | |
tree | 1d4215798009e86bdaa632d963e0e1255f4aeee5 | |
parent | 6b20a5101efcd554f96bc1a0d32dd38415e627f5 (diff) | |
download | nspr-hg-37bfd11ac779147a379619221da90fe589b8ac44.tar.gz |
NSPR 3.5 landing
121 files changed, 9079 insertions, 1048 deletions
diff --git a/config/AIX.mk b/config/AIX.mk index 0e68d833..bd40a9be 100644 --- a/config/AIX.mk +++ b/config/AIX.mk @@ -57,6 +57,11 @@ else endif endif +# IPv6 support part of the standard AIX 4.3 release. +ifneq (,$(filter-out 3.2 4.1 4.2,$(OS_RELEASE))) +USE_IPV6 = 1 +endif + ifeq ($(CLASSIC_NSPR),1) CC = xlC CCC = xlC diff --git a/config/HP-UX.mk b/config/HP-UX.mk index 5a23232b..371de60b 100644 --- a/config/HP-UX.mk +++ b/config/HP-UX.mk @@ -120,10 +120,10 @@ ifeq ($(OS_RELEASE),B.11.00) ifndef NS_USE_GCC CCC = /opt/aCC/bin/aCC -ext ifeq ($(USE_64), 1) - OS_CFLAGS += +DA2.0W +DChpux + OS_CFLAGS += +DA2.0W +DS2.0 COMPILER_TAG = _64 else - OS_CFLAGS += +DAportable +DS1.1 + OS_CFLAGS += +DAportable +DS2.0 COMPILER_TAG = _32 endif endif @@ -176,7 +176,7 @@ endif MKSHLIB = $(LD) $(DSO_LDOPTS) -DSO_LDOPTS = -b +DSO_LDOPTS = -b +h $(notdir $@) DSO_LDFLAGS = # -fPIC or +Z generates position independent code for use in shared @@ -186,5 +186,3 @@ DSO_CFLAGS = -fPIC else DSO_CFLAGS = +Z endif - -HAVE_PURIFY = 1 diff --git a/config/IRIX.mk b/config/IRIX.mk index db26b010..44b86fd1 100644 --- a/config/IRIX.mk +++ b/config/IRIX.mk @@ -134,12 +134,6 @@ endif # -rdata_shared is an ld option that puts string constants and # const data into the text segment, where they will be shared # across processes and be read-only. -MKSHLIB = $(LD) $(SHLIB_LD_OPTS) -rdata_shared -shared -soname $(@:$(OBJDIR)/%.so=%.so) - -HAVE_PURIFY = 1 +MKSHLIB = $(LD) $(SHLIB_LD_OPTS) -rdata_shared -shared -soname $(notdir $@) DSO_LDOPTS = -elf -shared -all - -ifdef DSO_BACKEND -DSO_LDOPTS += -soname $(DSO_NAME) -endif diff --git a/config/Linux.mk b/config/Linux.mk index 5fd85a46..be18254c 100644 --- a/config/Linux.mk +++ b/config/Linux.mk @@ -101,7 +101,7 @@ endif # Linux 2.x has shared libraries. # -MKSHLIB = $(LD) $(DSO_LDOPTS) -soname $(@:$(OBJDIR)/%.so=%.so) +MKSHLIB = $(LD) $(DSO_LDOPTS) -soname $(notdir $@) ifdef BUILD_OPT OPTIMIZER = -O2 endif diff --git a/config/Makefile b/config/Makefile index e52ce589..36ac7809 100644 --- a/config/Makefile +++ b/config/Makefile @@ -84,11 +84,9 @@ PROGS += $(OBJDIR)/nsinstall$(PROG_SUFFIX) TARGETS = $(PROGS) $(PLSRCS:.pl=) endif -OUTOPTION = -o -ifeq ($(OS_ARCH), WINNT) -ifeq ($(CPU_ARCH),ALPHA) -OUTOPTION = /link /out: -endif +OUTOPTION = -o # end of the line +ifeq (,$(filter-out WINNT WIN95,$(OS_TARGET))) +OUTOPTION = /Fe endif # Redefine MAKE_OBJDIR for just this directory diff --git a/config/OSF1.mk b/config/OSF1.mk index ba9cd22b..e1c80c5e 100644 --- a/config/OSF1.mk +++ b/config/OSF1.mk @@ -93,5 +93,5 @@ OS_CFLAGS += -pthread endif # The command to build a shared library on OSF1. -MKSHLIB = ld -shared -all -expect_unresolved "*" +MKSHLIB = ld -shared -all -expect_unresolved "*" -soname $(notdir $@) DSO_LDOPTS = -shared diff --git a/config/SINIX.mk b/config/SINIX.mk index 664f6ae0..6f42e7d9 100644 --- a/config/SINIX.mk +++ b/config/SINIX.mk @@ -89,6 +89,4 @@ OS_CFLAGS = $(NOMD_OS_CFLAGS) OS_LIBS = -lsocket -lnsl -lresolv -ldl -lc NOSUCHFILE = /no-such-file -HAVE_PURIFY = 0 - DEFINES += -D_PR_LOCAL_THREADS_ONLY diff --git a/config/SunOS4.mk b/config/SunOS4.mk index 70273112..5f73db40 100644 --- a/config/SunOS4.mk +++ b/config/SunOS4.mk @@ -45,8 +45,6 @@ endif MKSHLIB = $(LD) $(DSO_LDOPTS) -HAVE_PURIFY = 1 - NOSUCHFILE = /no-such-file DSO_LDOPTS = diff --git a/config/SunOS5.mk b/config/SunOS5.mk index cf244de2..f7243bce 100644 --- a/config/SunOS5.mk +++ b/config/SunOS5.mk @@ -79,6 +79,18 @@ endif endif endif +ifeq ($(USE_64),1) +ifndef INTERNAL_TOOLS +ifndef NS_USE_GCC +CC += -xarch=v9 +CCC += -xarch=v9 +endif +endif +COMPILER_TAG = _64 +else +COMPILER_TAG = _32 +endif + RANLIB = echo OS_DEFINES = -DSVR4 -DSYSV -D__svr4 -D__svr4__ -DSOLARIS @@ -132,7 +144,7 @@ MKSHLIB = $(LD) $(DSO_LDOPTS) # ld options: # -G: produce a shared object # -z defs: no unresolved symbols allowed -DSO_LDOPTS = -G +DSO_LDOPTS = -G -h $(notdir $@) # -KPIC generates position independent code for use in shared libraries. # (Similarly for -fPIC in case of gcc.) @@ -142,8 +154,6 @@ else DSO_CFLAGS = -KPIC endif -HAVE_PURIFY = 1 - NOSUCHFILE = /no-such-file # diff --git a/config/WIN32.mk b/config/WIN32.mk index 8c4fc172..375c80c3 100644 --- a/config/WIN32.mk +++ b/config/WIN32.mk @@ -53,7 +53,14 @@ OS_CFLAGS = -W3 -nologo -GF -Gy ifdef BUILD_OPT OS_CFLAGS += -MD +# The -O2 optimization of MSVC 6.0 SP3 appears to generate +# code that is unsafe for our use of fibers and static thread +# local storage. We temporarily work around this problem by +# turning off global optimizations (-Og). OPTIMIZER = -O2 +ifeq ($(OS_TARGET),WINNT) +OPTIMIZER += -Og- +endif DEFINES = -UDEBUG -U_DEBUG -DNDEBUG DLLFLAGS = -OUT:"$@" OBJDIR_TAG = _OPT diff --git a/config/config.mk b/config/config.mk index 45fdf764..91bba205 100644 --- a/config/config.mk +++ b/config/config.mk @@ -130,7 +130,7 @@ endif MDIST = /m/dist ifeq ($(OS_ARCH),WINNT) MDIST = //helium/dist -MDIST_DOS = \\\\helium\\dist +MDIST_DOS = $(subst /,\\,$(MDIST)) endif # RELEASE_DIR is ns/dist/<module name> diff --git a/config/rules.mk b/config/rules.mk index 8aff5b8d..9be1c121 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -58,7 +58,6 @@ endif # - LIBRARY: a static (archival) library # - SHARED_LIBRARY: a shared (dynamic link) library # - IMPORT_LIBRARY: an import library, used only on Windows and OS/2 -# - PURE_LIBRARY: a library for Purify # # The names of these libraries can be generated by simply specifying # LIBRARY_NAME and LIBRARY_VERSION. @@ -91,13 +90,6 @@ ifdef MKSHLIB SHARED_LIBRARY = $(OBJDIR)/lib$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX) endif endif -ifdef HAVE_PURIFY -ifdef DSO_BACKEND -PURE_LIBRARY = $(OBJDIR)/purelib$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX) -else -PURE_LIBRARY = $(OBJDIR)/purelib$(LIBRARY_NAME)$(LIBRARY_VERSION).$(LIB_SUFFIX) -endif -endif endif endif @@ -107,9 +99,6 @@ ifeq ($(OS_ARCH), WINNT) TARGETS = $(LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) else TARGETS = $(LIBRARY) $(SHARED_LIBRARY) -ifdef HAVE_PURIFY -TARGETS += $(PURE_LIBRARY) -endif endif endif @@ -334,13 +323,6 @@ endif # OpenVMS endif # WINNT endif # AIX 4.1 -$(PURE_LIBRARY): - rm -f $@ -ifneq ($(OS_ARCH), WINNT) - $(AR) $(OBJS) -endif - $(RANLIB) $@ - ifeq ($(OS_ARCH), WINNT) $(RES): $(RESNAME) @$(MAKE_OBJDIR) diff --git a/lib/ds/plevent.c b/lib/ds/plevent.c index e337d7fb..de1302c7 100644 --- a/lib/ds/plevent.c +++ b/lib/ds/plevent.c @@ -22,10 +22,8 @@ typedef MPARAM WPARAM,LPARAM; #endif /* XP_OS2 */ +#include "primpl.h" #include "plevent.h" -#include "prmem.h" -#include "prcmon.h" -#include "prlog.h" #if !defined(WIN32) #include <errno.h> @@ -43,11 +41,6 @@ typedef MPARAM WPARAM,LPARAM; #if defined(XP_MAC) #include <AppleEvents.h> -#include "pprthred.h" -#include "primpl.h" -#else -#include "private/pprthred.h" -#include "private/primpl.h" #endif /* XP_MAC */ #if defined(VMS) diff --git a/lib/ds/plvrsion.c b/lib/ds/plvrsion.c index acdfac95..bc5ee7dd 100644 --- a/lib/ds/plvrsion.c +++ b/lib/ds/plvrsion.c @@ -36,6 +36,11 @@ #if !defined(_PRODUCTION) #define _PRODUCTION "" #endif +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif static PRVersionDescription prVersionDescription_libplds3 = { @@ -65,8 +70,10 @@ static PRVersionDescription prVersionDescription_libplds3 = /* * Version information for the 'ident' and 'what commands */ -static char rcsid[] = "$Version: NSPR " PR_VERSION " " _BUILD_STRING " $"; -static char sccsid[] = "@(#)NSPR " PR_VERSION " " _BUILD_STRING; +static char rcsid[] = "$Version: NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING " $"; +static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING; #endif /* XP_UNIX */ diff --git a/lib/libc/src/plvrsion.c b/lib/libc/src/plvrsion.c index c09e5363..70dff093 100644 --- a/lib/libc/src/plvrsion.c +++ b/lib/libc/src/plvrsion.c @@ -36,6 +36,11 @@ #if !defined(_PRODUCTION) #define _PRODUCTION "" #endif +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif static PRVersionDescription prVersionDescription_libplc3 = { @@ -65,8 +70,10 @@ static PRVersionDescription prVersionDescription_libplc3 = /* * Version information for the 'ident' and 'what commands */ -static char rcsid[] = "$Version: NSPR " PR_VERSION " " _BUILD_STRING " $"; -static char sccsid[] = "@(#)NSPR " PR_VERSION " " _BUILD_STRING; +static char rcsid[] = "$Version: NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING " $"; +static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING; #endif /* XP_UNIX */ diff --git a/lib/prstreams/plvrsion.c b/lib/prstreams/plvrsion.c index 66a953fd..9a187c97 100644 --- a/lib/prstreams/plvrsion.c +++ b/lib/prstreams/plvrsion.c @@ -36,6 +36,11 @@ #if !defined(_PRODUCTION) #define _PRODUCTION "" #endif +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif static PRVersionDescription prVersionDescription_libprstrms3 = { @@ -65,8 +70,10 @@ static PRVersionDescription prVersionDescription_libprstrms3 = /* * Version information for the 'ident' and 'what commands */ -static char rcsid[] = "$Version: NSPR " PR_VERSION " " _BUILD_STRING " $"; -static char sccsid[] = "@(#)NSPR " PR_VERSION " " _BUILD_STRING; +static char rcsid[] = "$Version: NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING " $"; +static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING; #endif /* XP_UNIX */ diff --git a/macbuild/NSPR20PPC.mcp b/macbuild/NSPR20PPC.mcp Binary files differindex 6c52af47..f890cb1a 100644 --- a/macbuild/NSPR20PPC.mcp +++ b/macbuild/NSPR20PPC.mcp diff --git a/pr/include/MANIFEST b/pr/include/MANIFEST index fbc544a5..6275f3d7 100644 --- a/pr/include/MANIFEST +++ b/pr/include/MANIFEST @@ -17,6 +17,7 @@ prinet.h prinit.h prinrval.h prio.h +pripcsem.h prlink.h prlock.h prlog.h @@ -30,6 +31,8 @@ prpdce.h prprf.h prproces.h prrwlock.h +prshm.h +prshma.h prsystem.h prthread.h prtime.h diff --git a/pr/include/md/Makefile b/pr/include/md/Makefile index 3acaa418..dfee019b 100644 --- a/pr/include/md/Makefile +++ b/pr/include/md/Makefile @@ -51,9 +51,13 @@ ifeq ($(OS_ARCH),AIX) ifeq ($(USE_64),1) MDCPUCFG_H = _aix64.cfg else +ifeq ($(USE_IPV6),1) +MDCPUCFG_H = _aix32in6.cfg +else MDCPUCFG_H = _aix32.cfg endif endif +endif ifeq ($(OS_ARCH),BSD_OS) MDCPUCFG_H = _bsdi.cfg @@ -95,7 +99,11 @@ ifeq ($(OS_ARCH),SunOS) ifeq ($(OS_RELEASE),4.1.3_U1) MDCPUCFG_H = _sunos4.cfg else -MDCPUCFG_H = _solaris.cfg +ifeq ($(USE_64),1) +MDCPUCFG_H = _solaris64.cfg +else +MDCPUCFG_H = _solaris32.cfg +endif endif endif diff --git a/pr/include/md/_aix.h b/pr/include/md/_aix.h index 011c756e..a80ccb06 100644 --- a/pr/include/md/_aix.h +++ b/pr/include/md/_aix.h @@ -54,6 +54,11 @@ #define _PR_POLL_AVAILABLE #define _PR_USE_POLL #define _PR_STAT_HAS_ONLY_ST_ATIME +#ifdef _PR_INET6 +#define _PR_HAVE_GETHOSTBYNAME2 +#endif +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY /* Timer operations */ #if defined(AIX_TIMERS) @@ -72,7 +77,9 @@ extern PRIntervalTime _MD_AixIntervalPerSec(void); /* The atomic operations */ #include <sys/atomic_op.h> #define _PR_HAVE_ATOMIC_OPS +#ifndef IS_64 #define _PR_HAVE_ATOMIC_CAS +#endif #define _MD_INIT_ATOMIC() #define _MD_ATOMIC_INCREMENT(val) ((PRInt32)fetch_and_add((atomic_p)val, 1) + 1) #define _MD_ATOMIC_ADD(ptr, val) ((PRInt32)fetch_and_add((atomic_p)ptr, val) + val) diff --git a/pr/include/md/_aix32in6.cfg b/pr/include/md/_aix32in6.cfg new file mode 100644 index 00000000..05fe241f --- /dev/null +++ b/pr/include/md/_aix32in6.cfg @@ -0,0 +1,125 @@ +/* -*- 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.1 (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_UNIX +#define XP_UNIX +#endif + +#ifndef AIX +#define AIX +#endif + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define HAVE_LONG_LONG +#undef HAVE_ALIGNED_DOUBLES +#undef HAVE_ALIGNED_LONGLONGS + +#ifndef _PR_INET6 +#define _PR_INET6 +#endif + +#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/include/md/_aix64.cfg b/pr/include/md/_aix64.cfg index e93d0130..30ad04d9 100644 --- a/pr/include/md/_aix64.cfg +++ b/pr/include/md/_aix64.cfg @@ -74,6 +74,10 @@ #undef HAVE_ALIGNED_DOUBLES #undef HAVE_ALIGNED_LONGLONGS +#ifndef _PR_INET6 +#define _PR_INET6 +#endif + #ifndef NO_NSPR_10_SUPPORT #define BYTES_PER_BYTE PR_BYTES_PER_BYTE diff --git a/pr/include/md/_beos.h b/pr/include/md/_beos.h index 310fe307..2e77f242 100644 --- a/pr/include/md/_beos.h +++ b/pr/include/md/_beos.h @@ -318,7 +318,6 @@ struct protoent* getprotobynumber(int number); #define _MD_RECV _MD_recv #define _MD_SEND _MD_send #define _MD_ACCEPT_READ _MD_accept_read -#define _MD_TRANSMITFILE _MD_transmitfile #define _MD_GETSOCKNAME _MD_getsockname #define _MD_GETPEERNAME _MD_getpeername #define _MD_GETSOCKOPT _MD_getsockopt @@ -514,7 +513,6 @@ PR_EXTERN(PRInt32) _MD_accept_read(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr * // PR_EXTERN(PRInt32) _MD_fast_accept(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen, PRIntervalTime timeout, PRBool fast, _PR_AcceptTimeoutCallback callback, void *callbackArg); // PR_EXTERN(PRInt32) _MD_fast_accept_read(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, void *buf, PRInt32 amount, PRIntervalTime timeout, PRBool fast, _PR_AcceptTimeoutCallback callback, void *callbackArg); // PR_EXTERN(void) _MD_update_accept_context(PRInt32 s, PRInt32 ls); -PR_EXTERN(PRInt32) _MD_transmitfile(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen, PRInt32 flags, PRIntervalTime timeout); PR_EXTERN(PRStatus) _MD_getsockname(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen); PR_EXTERN(PRStatus) _MD_getpeername(PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen); PR_EXTERN(PRStatus) _MD_getsockopt(PRFileDesc *fd, PRInt32 level, PRInt32 optname, char* optval, PRInt32* optlen); diff --git a/pr/include/md/_hpux.h b/pr/include/md/_hpux.h index 12895c63..0d45f6c3 100644 --- a/pr/include/md/_hpux.h +++ b/pr/include/md/_hpux.h @@ -46,6 +46,8 @@ #define _PR_POLL_AVAILABLE #define _PR_USE_POLL #define _PR_STAT_HAS_ONLY_ST_ATIME +#define _PR_HAVE_POSIX_SEMAPHORES +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY #undef _PR_HAVE_ATOMIC_OPS diff --git a/pr/include/md/_irix.h b/pr/include/md/_irix.h index 720991ac..4da14559 100644 --- a/pr/include/md/_irix.h +++ b/pr/include/md/_irix.h @@ -58,6 +58,8 @@ #define _PR_STAT_HAS_ST_ATIM #define _PR_HAVE_OFF64_T #define HAVE_POINTER_LOCALTIME_R +#define _PR_HAVE_POSIX_SEMAPHORES +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY /* Initialization entry points */ PR_EXTERN(void) _MD_EarlyInit(void); diff --git a/pr/include/md/_linux.h b/pr/include/md/_linux.h index 16a772b2..7980934c 100644 --- a/pr/include/md/_linux.h +++ b/pr/include/md/_linux.h @@ -70,6 +70,8 @@ #else #define _PR_NO_LARGE_FILES #endif +#define _PR_HAVE_SYSV_SEMAPHORES +#define PR_HAVE_SYSV_NAMED_SHARED_MEMORY #ifdef _PR_PTHREADS diff --git a/pr/include/md/_os2.h b/pr/include/md/_os2.h index f4fee3d3..5d7ef0fc 100644 --- a/pr/include/md/_os2.h +++ b/pr/include/md/_os2.h @@ -261,7 +261,6 @@ extern PRInt32 _MD_CloseSocket(PRInt32 osfd); #define _MD_ATOMIC_SET(x,y) _PR_MD_ATOMIC_SET(x, y) #define _MD_INIT_IO (_PR_MD_INIT_IO) -#define _MD_TRANSMITFILE (_PR_MD_TRANSMITFILE) #define _MD_PR_POLL (_PR_MD_PR_POLL) /* win95 doesn't have async IO */ diff --git a/pr/include/md/_osf1.h b/pr/include/md/_osf1.h index 04860b68..7947ec61 100644 --- a/pr/include/md/_osf1.h +++ b/pr/include/md/_osf1.h @@ -46,6 +46,12 @@ #define _PR_USE_POLL #define _PR_STAT_HAS_ONLY_ST_ATIME #define _PR_HAVE_LARGE_OFF_T +#ifdef _PR_INET6 +#define _PR_HAVE_GETIPNODEBYNAME +#define _PR_HAVE_GETIPNODEBYADDR +#endif +#define _PR_HAVE_POSIX_SEMAPHORES +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY #define USE_SETJMP diff --git a/pr/include/md/_solaris.h b/pr/include/md/_solaris.h index 467607ce..11681813 100644 --- a/pr/include/md/_solaris.h +++ b/pr/include/md/_solaris.h @@ -44,16 +44,34 @@ #define USE_DLFCN #define NEED_STRFTIME_LOCK -#ifdef _PR_LOCAL_THREADS_ONLY -#undef _PR_HAVE_ATOMIC_OPS -#else +/* + * Sparc v8 does not have instructions to efficiently implement + * atomic increment/decrement operations. In the local threads + * only and pthreads versions, we use the default atomic routine + * implementation in pratom.c. The obsolete global threads only + * version uses a global mutex_t to implement the atomic routines + * in solaris.c, which is actually equivalent to the default + * implementation. + */ +#ifdef _PR_GLOBAL_THREADS_ONLY #define _PR_HAVE_ATOMIC_OPS +#endif + +#if defined(_PR_GLOBAL_THREADS_ONLY) || defined(_PR_PTHREADS) +/* + * We have assembly language implementation of atomic + * stacks for the 32-bit sparc and x86 architectures only. + */ +#if !defined(sparc) || !defined(IS_64) #define _PR_HAVE_ATOMIC_CAS #endif +#endif #define _PR_POLL_AVAILABLE #define _PR_USE_POLL #define _PR_STAT_HAS_ST_ATIM +#define _PR_HAVE_POSIX_SEMAPHORES +#define PR_HAVE_POSIX_NAMED_SHARED_MEMORY #include "prinrval.h" PR_EXTERN(PRIntervalTime) _MD_Solaris_GetInterval(void); diff --git a/pr/include/md/_solaris.cfg b/pr/include/md/_solaris32.cfg index f6b988a1..f6b988a1 100644 --- a/pr/include/md/_solaris.cfg +++ b/pr/include/md/_solaris32.cfg diff --git a/pr/include/md/_solaris64.cfg b/pr/include/md/_solaris64.cfg new file mode 100644 index 00000000..40832724 --- /dev/null +++ b/pr/include/md/_solaris64.cfg @@ -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.1 (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_UNIX +#define XP_UNIX +#endif + +#ifndef SOLARIS +#define SOLARIS +#endif + +#ifdef sparc +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_DOUBLE 8 +#elif defined(i386) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_DOUBLE 4 +#else +#error unknown processor +#endif +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_POINTER 8 + +#define HAVE_LONG_LONG +#define HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS + +#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 /* ifndef nspr_cpucfg___ */ diff --git a/pr/include/md/_unix_errors.h b/pr/include/md/_unix_errors.h index 23a6d84c..d911081e 100644 --- a/pr/include/md/_unix_errors.h +++ b/pr/include/md/_unix_errors.h @@ -24,8 +24,8 @@ PR_BEGIN_EXTERN_C -PR_EXTERN(PRStatus) _MD_gethostname(char *name, PRUint32 namelen); -#define _MD_GETHOSTNAME _MD_gethostname +PR_EXTERN(void) _MD_unix_map_default_error(int err); +#define _PR_MD_MAP_DEFAULT_ERROR _MD_unix_map_default_error PR_EXTERN(void) _MD_unix_map_opendir_error(int err); #define _PR_MD_MAP_OPENDIR_ERROR _MD_unix_map_opendir_error diff --git a/pr/include/md/_unixos.h b/pr/include/md/_unixos.h index 10b3c6be..53684bbe 100644 --- a/pr/include/md/_unixos.h +++ b/pr/include/md/_unixos.h @@ -324,8 +324,7 @@ extern PRInt32 _MD_mkdir(const char *name, PRIntn mode); extern PRInt32 _MD_rmdir(const char *name); extern PRInt32 _MD_accept_read(PRInt32 sock, PRInt32 *newSock, PRNetAddr **raddr, void *buf, PRInt32 amount); -extern PRInt32 _PR_UnixTransmitFile(PRFileDesc *sd, PRFileDesc *fd, - const void *headers, PRInt32 hlen, +extern PRInt32 _PR_UnixSendFile(PRFileDesc *sd, PRSendFileData *sfd, PRTransmitFileFlags flags, PRIntervalTime timeout); extern PRStatus _MD_LockFile(PRInt32 osfd); @@ -446,6 +445,7 @@ extern int _MD_unix_get_nonblocking_connect_error(int osfd); struct _MDFileMap { PRIntn prot; PRIntn flags; + PRBool isAnonFM; /* when true, PR_CloseFileMap() must close the related fd */ }; extern PRStatus _MD_CreateFileMap(struct PRFileMap *fmap, PRInt64 size); diff --git a/pr/include/md/_win16.h b/pr/include/md/_win16.h index 49b34415..544b4564 100644 --- a/pr/include/md/_win16.h +++ b/pr/include/md/_win16.h @@ -302,7 +302,6 @@ extern PRStatus _PR_KillWindowsProcess(struct PRProcess *process); #define _MD_ATOMIC_SET(x,y) (*x, y) #define _MD_INIT_IO _PR_MD_INIT_IO -#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE /* win95 doesn't have async IO */ #define _MD_SOCKET _PR_MD_SOCKET diff --git a/pr/include/md/_win32_errors.h b/pr/include/md/_win32_errors.h index 0601c06e..2ff0abe1 100644 --- a/pr/include/md/_win32_errors.h +++ b/pr/include/md/_win32_errors.h @@ -24,6 +24,9 @@ #include <errno.h> +extern void _MD_win32_map_default_error(PRInt32 err); +#define _PR_MD_MAP_DEFAULT_ERROR _MD_win32_map_default_error + extern void _MD_win32_map_opendir_error(PRInt32 err); #define _PR_MD_MAP_OPENDIR_ERROR _MD_win32_map_opendir_error diff --git a/pr/include/md/_win95.h b/pr/include/md/_win95.h index f9ee38b0..89febac7 100644 --- a/pr/include/md/_win95.h +++ b/pr/include/md/_win95.h @@ -38,6 +38,7 @@ #define HAVE_SOCKET_REUSEADDR #define HAVE_SOCKET_KEEPALIVE #define _PR_HAVE_ATOMIC_OPS +#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY /* --- Common User-Thread/Native-Thread Definitions --------------------- */ @@ -240,7 +241,6 @@ extern PRInt32 _MD_CloseSocket(PRInt32 osfd); #define _MD_ATOMIC_SET(x,y) InterlockedExchange((PLONG)x, (LONG)y) #define _MD_INIT_IO _PR_MD_INIT_IO -#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE /* win95 doesn't have async IO */ @@ -436,4 +436,12 @@ extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); #define _MD_CLOSE_FILE_MAP _MD_CloseFileMap +/* --- Named semaphores stuff --- */ +#define _PR_HAVE_NAMED_SEMAPHORES +#define _MD_OPEN_SEMAPHORE _PR_MD_OPEN_SEMAPHORE +#define _MD_WAIT_SEMAPHORE _PR_MD_WAIT_SEMAPHORE +#define _MD_POST_SEMAPHORE _PR_MD_POST_SEMAPHORE +#define _MD_CLOSE_SEMAPHORE _PR_MD_CLOSE_SEMAPHORE +#define _MD_DELETE_SEMAPHORE(name) PR_SUCCESS /* no op */ + #endif /* nspr_win32_defs_h___ */ diff --git a/pr/include/md/_winnt.h b/pr/include/md/_winnt.h index 0c2f438e..c75aa9b5 100644 --- a/pr/include/md/_winnt.h +++ b/pr/include/md/_winnt.h @@ -51,6 +51,7 @@ #define HAVE_SOCKET_KEEPALIVE #define _PR_HAVE_ATOMIC_OPS #define _PR_HAVE_ATOMIC_CAS +#define PR_HAVE_WIN32_NAMED_SHARED_MEMORY /* --- Common User-Thread/Native-Thread Definitions --------------------- */ @@ -85,7 +86,6 @@ typedef struct _MDOverlapped { enum _MDIOModel ioModel; /* The I/O model to implement * using overlapped I/O. */ - union { struct _MDThread *mdThread; /* For blocking I/O, this structure * is embedded in the _MDThread @@ -120,6 +120,10 @@ struct _MDThread { void *sp; /* only valid when suspended */ PRUint32 magic; /* for debugging */ PR_CONTEXT_TYPE gcContext; /* Thread context for GC */ + struct _PRCPU *thr_bound_cpu; /* thread bound to cpu */ + PRBool interrupt_disabled;/* thread cannot be interrupted */ + HANDLE thr_event; /* For native-threads-only support, + thread blocks on this event */ /* The following are used only if this is a fiber */ void *fiber_id; /* flag whether or not this is a fiber*/ @@ -194,6 +198,7 @@ struct _MDProcess { DWORD id; }; + /* --- Misc stuff --- */ #define _MD_GET_SP(thread) (thread)->md.gcContext[6] @@ -275,7 +280,7 @@ extern int _PR_NTFiberSafeSelect(int, fd_set *, fd_set *, fd_set *, #define _MD_BIND _PR_MD_BIND #define _MD_RECV _PR_MD_RECV #define _MD_SEND _PR_MD_SEND -#define _MD_TRANSMITFILE _PR_MD_TRANSMITFILE +#define _MD_SENDFILE _PR_MD_SENDFILE #define _MD_PR_POLL _PR_MD_PR_POLL /* --- Scheduler stuff --- */ @@ -327,7 +332,7 @@ extern void _PR_Unblock_IO_Wait(PRThread *thr); #define _MD_FREE_LOCK(lock) DeleteCriticalSection(&((lock)->mutex)) #ifndef PROFILE_LOCKS #define _MD_LOCK(lock) EnterCriticalSection(&((lock)->mutex)) -#define _MD_TEST_AND_LOCK(lock) 0 /* XXXMB */ +#define _MD_TEST_AND_LOCK(lock) (TryEnterCriticalSection(&((lock)->mutex))== FALSE) #define _MD_UNLOCK(lock) LeaveCriticalSection(&((lock)->mutex)) #else #define _MD_LOCK(lock) \ @@ -505,4 +510,12 @@ extern PRStatus _MD_MemUnmap(void *addr, PRUint32 size); extern PRStatus _MD_CloseFileMap(struct PRFileMap *fmap); #define _MD_CLOSE_FILE_MAP _MD_CloseFileMap +/* --- Named semaphores stuff --- */ +#define _PR_HAVE_NAMED_SEMAPHORES +#define _MD_OPEN_SEMAPHORE _PR_MD_OPEN_SEMAPHORE +#define _MD_WAIT_SEMAPHORE _PR_MD_WAIT_SEMAPHORE +#define _MD_POST_SEMAPHORE _PR_MD_POST_SEMAPHORE +#define _MD_CLOSE_SEMAPHORE _PR_MD_CLOSE_SEMAPHORE +#define _MD_DELETE_SEMAPHORE(name) PR_SUCCESS /* no op */ + #endif /* nspr_win32_defs_h___ */ diff --git a/pr/include/nspr.h b/pr/include/nspr.h index 7dcffe63..691b6a5b 100644 --- a/pr/include/nspr.h +++ b/pr/include/nspr.h @@ -31,6 +31,7 @@ #include "prinit.h" #include "prinrval.h" #include "prio.h" +#include "pripcsem.h" #include "prlink.h" #include "prlock.h" #include "prlog.h" @@ -42,6 +43,8 @@ #include "prprf.h" #include "prproces.h" #include "prrwlock.h" +#include "prshm.h" +#include "prshma.h" #include "prsystem.h" #include "prthread.h" #include "prtime.h" diff --git a/pr/include/prinit.h b/pr/include/prinit.h index 329a5058..b2119119 100644 --- a/pr/include/prinit.h +++ b/pr/include/prinit.h @@ -44,9 +44,9 @@ PR_BEGIN_EXTERN_C ** The format of the version string is ** "<major version>.<minor version> <build date>" */ -#define PR_VERSION "3.1" +#define PR_VERSION "3.5" #define PR_VMAJOR 3 -#define PR_VMINOR 1 +#define PR_VMINOR 5 #define PR_VPATCH 0 #define PR_BETA PR_FALSE @@ -213,11 +213,3 @@ PR_EXTERN(PRStatus) PR_CallOnce( PR_END_EXTERN_C #endif /* prinit_h___ */ - - - - - - - - diff --git a/pr/include/prio.h b/pr/include/prio.h index 76575c2b..5ced4fb4 100644 --- a/pr/include/prio.h +++ b/pr/include/prio.h @@ -156,6 +156,7 @@ union PRNetAddr { PRUint16 port; /* port number */ PRUint32 flowinfo; /* routing information */ PRIPv6Addr ip; /* the actual 128 bits of address */ + PRUint32 scope_id; /* set of interfaces for a scope */ } ipv6; #endif /* defined(_PR_INET6) */ #if defined(XP_UNIX) @@ -1044,7 +1045,7 @@ PR_EXTERN(PRUintn) PR_NetAddrSize(const PRNetAddr* addr); ************************************************************************* * FUNCTION: PR_NewUDPSocket * DESCRIPTION: - * Create a new UDP network connection. + * Create a new UDP socket. * INPUTS: * None * OUTPUTS: @@ -1052,7 +1053,7 @@ PR_EXTERN(PRUintn) PR_NetAddrSize(const PRNetAddr* addr); * RETURN: PRFileDesc* * Upon successful completion, PR_NewUDPSocket returns a pointer * to the PRFileDesc created for the newly opened UDP socket. - * Returns a NULL pointer if the create of a new UDP connection failed. + * Returns a NULL pointer if the creation of a new UDP socket failed. * ************************************************************************** */ @@ -1063,7 +1064,7 @@ PR_EXTERN(PRFileDesc*) PR_NewUDPSocket(void); ************************************************************************* * FUNCTION: PR_NewTCPSocket * DESCRIPTION: - * Create a new TCP network connection. + * Create a new TCP socket. * INPUTS: * None * OUTPUTS: @@ -1071,7 +1072,7 @@ PR_EXTERN(PRFileDesc*) PR_NewUDPSocket(void); * RETURN: PRFileDesc* * Upon successful completion, PR_NewTCPSocket returns a pointer * to the PRFileDesc created for the newly opened TCP socket. - * Returns a NULL pointer if the create of a new TCP connection failed. + * Returns a NULL pointer if the creation of a new TCP socket failed. * ************************************************************************** */ @@ -1080,6 +1081,46 @@ PR_EXTERN(PRFileDesc*) PR_NewTCPSocket(void); /* ************************************************************************* + * FUNCTION: PR_OpenUDPSocket + * DESCRIPTION: + * Create a new UDP socket of the specified address family. + * INPUTS: + * PRIntn af + * Address family + * OUTPUTS: + * None + * RETURN: PRFileDesc* + * Upon successful completion, PR_OpenUDPSocket returns a pointer + * to the PRFileDesc created for the newly opened UDP socket. + * Returns a NULL pointer if the creation of a new UDP socket failed. + * + ************************************************************************** + */ + +PR_EXTERN(PRFileDesc*) PR_OpenUDPSocket(PRIntn af); + +/* + ************************************************************************* + * FUNCTION: PR_OpenTCPSocket + * DESCRIPTION: + * Create a new TCP socket of the specified address family. + * INPUTS: + * PRIntn af + * Address family + * OUTPUTS: + * None + * RETURN: PRFileDesc* + * Upon successful completion, PR_NewTCPSocket returns a pointer + * to the PRFileDesc created for the newly opened TCP socket. + * Returns a NULL pointer if the creation of a new TCP socket failed. + * + ************************************************************************** + */ + +PR_EXTERN(PRFileDesc*) PR_OpenTCPSocket(PRIntn af); + +/* + ************************************************************************* * FUNCTION: PR_Connect * DESCRIPTION: * Initiate a connection on a socket. @@ -1402,6 +1443,58 @@ PR_EXTERN(PRInt32) PR_TransmitFile( PRFileDesc *networkSocket, PRFileDesc *sourceFile, const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, PRIntervalTime timeout); + +/* +************************************************************************* +** FUNCTION: PR_SendFile +** DESCRIPTION: +** PR_SendFile sends data from a file (sendData->fd) across a socket +** (networkSocket). If specified, a header and/or trailer buffer are sent +** before and after the file, respectively. The file offset, number of bytes +** of file data to send, the header and trailer buffers are specified in the +** sendData argument. +** +** Optionally, if the PR_TRANSMITFILE_CLOSE_SOCKET flag is passed, the +** socket is closed after successfully sending the data. +** +** INPUTS: +** PRFileDesc *networkSocket +** The socket to send data over +** PRSendFileData *sendData +** Contains the FD, file offset and length, header and trailer +** buffer specifications. +** PRTransmitFileFlags flags +** If the flags indicate that the connection should be closed, +** it will be done immediately after transferring the file, unless +** the operation is unsuccessful. +.* PRIntervalTime timeout + * Time limit for completion of the send operation. +** +** RETURNS: +** Returns the number of bytes written or -1 if the operation failed. +** If an error occurs while sending the file, the PR_TRANSMITFILE_CLOSE_ +** SOCKET flag is ignored. The reason for the failure is obtained +** by calling PR_GetError(). +************************************************************************** +*/ + +typedef struct PRSendFileData { + PRFileDesc *fd; /* file to send */ + PRUint32 file_offset; /* file offset */ + PRSize file_nbytes; /* number of bytes of file data to send */ + /* if 0, send data from file_offset to */ + /* end-of-file. */ + const void *header; /* header buffer */ + PRInt32 hlen; /* header len */ + const void *trailer; /* trailer buffer */ + PRInt32 tlen; /* trailer len */ +} PRSendFileData; + + +PR_EXTERN(PRInt32) PR_SendFile( + PRFileDesc *networkSocket, PRSendFileData *sendData, + PRTransmitFileFlags flags, PRIntervalTime timeout); + /* ************************************************************************* ** FUNCTION: PR_AcceptRead diff --git a/pr/include/pripcsem.h b/pr/include/pripcsem.h new file mode 100644 index 00000000..c123afea --- /dev/null +++ b/pr/include/pripcsem.h @@ -0,0 +1,113 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * File: pripcsem.h + * + * Description: named semaphores for interprocess + * synchronization + * + * Unrelated processes obtain access to a shared semaphore + * by specifying its name. + * + * Our goal is to support named semaphores on at least + * Unix and Win32 platforms. The implementation will use + * one of the three native semaphore APIs: POSIX, System V, + * and Win32. + * + * Because POSIX named semaphores have kernel persistence, + * we are forced to have a delete function in this API. + */ + +#ifndef pripcsem_h___ +#define pripcsem_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* + * PRSem is an opaque structure that represents a named + * semaphore. + */ +typedef struct PRSem PRSem; + +/* + * PR_OpenSemaphore -- + * + * Create or open a named semaphore with the specified name. + * A handle to the semaphore is returned. + * + * If the named semaphore doesn't exist and the PR_SEM_CREATE + * flag is specified, the named semaphore is created. The + * created semaphore needs to be removed from the system with + * a PR_DeleteSemaphore call. + * + * If PR_SEM_CREATE is specified, the third argument is the + * access permission bits of the new semaphore (same + * interpretation as the mode argument to PR_Open) and the + * fourth argument is the initial value of the new semaphore. + * If PR_SEM_CREATE is not specified, the third and fourth + * arguments are ignored. + */ + +#define PR_SEM_CREATE 0x1 /* create if not exist */ +#define PR_SEM_EXCL 0x2 /* fail if already exists */ + +PR_EXTERN(PRSem *) PR_OpenSemaphore( + const char *name, PRIntn flags, PRIntn mode, PRUintn value); + +/* + * PR_WaitSemaphore -- + * + * If the value of the semaphore is > 0, decrement the value and return. + * If the value is 0, sleep until the value becomes > 0, then decrement + * the value and return. + * + * The "test and decrement" operation is performed atomically. + */ + +PR_EXTERN(PRStatus) PR_WaitSemaphore(PRSem *sem); + +/* + * PR_PostSemaphore -- + * + * Increment the value of the named semaphore by 1. + */ + +PR_EXTERN(PRStatus) PR_PostSemaphore(PRSem *sem); + +/* + * PR_CloseSemaphore -- + * + * Close a named semaphore handle. + */ + +PR_EXTERN(PRStatus) PR_CloseSemaphore(PRSem *sem); + +/* + * PR_DeleteSemaphore -- + * + * Remove a named semaphore from the system. + */ + +PR_EXTERN(PRStatus) PR_DeleteSemaphore(const char *name); + +PR_END_EXTERN_C + +#endif /* pripcsem_h___ */ diff --git a/pr/include/private/pprio.h b/pr/include/private/pprio.h index 08b44076..02dbcb9a 100644 --- a/pr/include/private/pprio.h +++ b/pr/include/private/pprio.h @@ -235,6 +235,13 @@ PR_EXTERN(void) PR_NTFast_UpdateAcceptContext(PRFileDesc *acceptSock, */ PR_EXTERN(void) PR_NT_UseNonblock(); +/* FUNCTION: PR_NT_CancelIo +** DESCRIPTION: +** Cancel IO operations on fd. +*/ +PR_EXTERN(PRStatus) PR_NT_CancelIo(PRFileDesc *fd); + + #endif /* WIN32 */ PR_END_EXTERN_C diff --git a/pr/include/private/primpl.h b/pr/include/private/primpl.h index 9716dd15..69a341ba 100644 --- a/pr/include/private/primpl.h +++ b/pr/include/private/primpl.h @@ -60,6 +60,12 @@ typedef struct PRSegment PRSegment; #include "obsolete/probslet.h" #endif /* XP_MAC */ +#ifdef _PR_HAVE_POSIX_SEMAPHORES +#include <semaphore.h> +#elif defined(_PR_HAVE_SYSV_SEMAPHORES) +#include <sys/sem.h> +#endif + /************************************************************************* ***** A Word about Model Dependent Function Naming Convention *********** *************************************************************************/ @@ -307,6 +313,8 @@ PR_EXTERN(PRInt32) _pr_intsOff; #endif /* _PR_LOCAL_THREADS_ONLY */ +extern PRInt32 _native_threads_only; + #if defined(_PR_GLOBAL_THREADS_ONLY) #define _MD_GET_INTSOFF() 0 @@ -973,6 +981,23 @@ extern char * _PR_MD_READ_DIR(_MDDir *md, PRIntn flags); extern PRInt32 _PR_MD_CLOSE_DIR(_MDDir *md); #define _PR_MD_CLOSE_DIR _MD_CLOSE_DIR +/* Named semaphores related */ +extern PRSem * _PR_MD_OPEN_SEMAPHORE( + const char *osname, PRIntn flags, PRIntn mode, PRUintn value); +#define _PR_MD_OPEN_SEMAPHORE _MD_OPEN_SEMAPHORE + +extern PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem); +#define _PR_MD_WAIT_SEMAPHORE _MD_WAIT_SEMAPHORE + +extern PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem); +#define _PR_MD_POST_SEMAPHORE _MD_POST_SEMAPHORE + +extern PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem); +#define _PR_MD_CLOSE_SEMAPHORE _MD_CLOSE_SEMAPHORE + +extern PRStatus _PR_MD_DELETE_SEMAPHORE(const char *osname); +#define _PR_MD_DELETE_SEMAPHORE _MD_DELETE_SEMAPHORE + /* I/O related */ extern void _PR_MD_INIT_FILEDESC(PRFileDesc *fd); #define _PR_MD_INIT_FILEDESC _MD_INIT_FILEDESC @@ -1084,14 +1109,12 @@ extern void _PR_MD_UPDATE_ACCEPT_CONTEXT(PRInt32 s, PRInt32 ls); #define _PR_MD_UPDATE_ACCEPT_CONTEXT _MD_UPDATE_ACCEPT_CONTEXT #endif /* WIN32 */ -extern PRInt32 _PR_MD_TRANSMITFILE( - PRFileDesc *sock, PRFileDesc *file, - const void *headers, PRInt32 hlen, PRInt32 flags, - PRIntervalTime timeout); -#define _PR_MD_TRANSMITFILE _MD_TRANSMITFILE -extern PRInt32 _PR_EmulateTransmitFile(PRFileDesc *sd, PRFileDesc *fd, - const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, - PRIntervalTime timeout); +extern PRInt32 _PR_MD_SENDFILE( + PRFileDesc *sock, PRSendFileData *sfd, + PRInt32 flags, PRIntervalTime timeout); +#define _PR_MD_SENDFILE _MD_SENDFILE +extern PRInt32 _PR_EmulateSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout); extern PRStatus _PR_MD_GETSOCKNAME( PRFileDesc *fd, PRNetAddr *addr, PRUint32 *addrlen); @@ -1294,6 +1317,20 @@ struct PRSemaphore { PR_EXTERN(void) _PR_InitSem(void); +/*************************************************************************/ + +struct PRSem { +#ifdef _PR_HAVE_POSIX_SEMAPHORES + sem_t *sem; +#elif defined(_PR_HAVE_SYSV_SEMAPHORES) + int semid; +#elif defined(WIN32) + HANDLE sem; +#else + PRInt8 notused; +#endif +}; + /************************************************************************/ /* XXX this needs to be exported (sigh) */ @@ -1355,6 +1392,7 @@ struct PRThread { PRBool okToDelete; /* ok to delete the PRThread struct? */ PRCondVar *io_cv; /* a condition used to run i/o */ PRCondVar *waiting; /* where the thread is waiting | NULL */ + PRIntn io_tq_index; /* the io-queue index for this thread */ void *sp; /* recorded sp for garbage collection */ PRThread *next, *prev; /* simple linked list of all threads */ PRUint32 suspend; /* used to store suspend and resume flags */ @@ -1462,20 +1500,39 @@ struct PRFilePrivate { PRInt32 state; PRBool nonblocking; PRBool inheritable; -#if defined(_PR_PTHREADS) - PRInt16 eventMask; /* A bitmask in which a 0 means - * the event should be ignored in - * the revents returned by poll. - * The eventMask field is only - * accessed by the i/o continuation - * thread. - */ -#endif PRFileDesc *next; PRIntn lockCount; _MDFileDesc md; +#ifdef _PR_PTHREADS + PRIntn eventMask[1]; /* An array of _pt_tq_count bitmasks. + * eventMask[i] is only accessed by + * the i-th i/o continuation thread. + * A 0 in a bitmask means the event + * should be igored in the revents + * bitmask returned by poll. + * + * poll's revents bitmask is a short, + * but we need to declare eventMask + * as an array of PRIntn's so that + * each bitmask can be updated + * individually without disturbing + * adjacent memory. Only the lower + * 16 bits of each PRIntn are used. */ +#endif +/* IMPORTANT: eventMask MUST BE THE LAST FIELD OF THIS STRUCTURE */ }; +/* + * The actual size of the PRFilePrivate structure, + * including the eventMask array at the end + */ +#ifdef _PR_PTHREADS +extern PRIntn _pt_tq_count; +#define PRFILEPRIVATE_SIZE (sizeof(PRFilePrivate) + (_pt_tq_count-1) * sizeof(PRIntn)) +#else +#define PRFILEPRIVATE_SIZE sizeof(PRFilePrivate) +#endif + struct PRDir { PRDirEntry d; _MDDir md; @@ -1712,6 +1769,97 @@ extern PRStatus _PR_MD_MEM_UNMAP(void *addr, PRUint32 size); extern PRStatus _PR_MD_CLOSE_FILE_MAP(PRFileMap *fmap); #define _PR_MD_CLOSE_FILE_MAP _MD_CLOSE_FILE_MAP +/* Named Shared Memory */ + +/* +** Declare PRSharedMemory. +*/ +struct PRSharedMemory +{ + char *ipcname; /* after conversion to native */ + PRSize size; /* from open */ + PRIntn mode; /* from open */ + PRIntn flags; /* from open */ +#if defined(PR_HAVE_POSIX_NAMED_SHARED_MEMORY) + int id; +#elif defined(PR_HAVE_SYSV_NAMED_SHARED_MEMORY) + int id; +#elif defined(PR_HAVE_WIN32_NAMED_SHARED_MEMORY) + HANDLE handle; +#else + PRUint32 nothing; /* placeholder, nothing behind here */ +#endif + PRUint32 ident; /* guard word at end of struct */ +#define _PR_SHM_IDENT 0xdeadbad +}; + +extern PRSharedMemory * _MD_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +); +#define _PR_MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory + +extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags ); +#define _PR_MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory + +extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr ); +#define _PR_MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory + +extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm ); +#define _PR_MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory + +extern PRStatus _MD_DeleteSharedMemory( const char *name ); +#define _PR_MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory + +extern PRFileMap* _md_OpenAnonFileMap( + const char *dirName, + PRSize size, + PRFileMapProtect prot +); +#define _PR_MD_OPEN_ANON_FILE_MAP _md_OpenAnonFileMap + +extern PRStatus _md_ExportFileMapAsString( + PRFileMap *fm, + PRSize bufSize, + char *buf +); +#define _PR_MD_EXPORT_FILE_MAP_AS_STRING _md_ExportFileMapAsString + +extern PRFileMap * _md_ImportFileMapFromString( + const char *fmstring +); +#define _PR_MD_IMPORT_FILE_MAP_FROM_STRING _md_ImportFileMapFromString + + + +/* Interprocess communications (IPC) */ + +/* + * The maximum length of an NSPR IPC name, including the + * terminating null byte. + */ +#define PR_IPC_NAME_SIZE 1024 + +/* + * Types of NSPR IPC objects + */ +typedef enum { + _PRIPCSem, /* semaphores */ + _PRIPCShm /* shared memory segments */ +} _PRIPCType; + +/* + * Make a native IPC name from an NSPR IPC name. + */ +extern PRStatus _PR_MakeNativeIPCName( + const char *name, /* NSPR IPC name */ + char *result, /* result buffer */ + PRIntn size, /* size of result buffer */ + _PRIPCType type /* type of IPC object */ +); + /* Socket call error code */ PR_EXTERN(PRInt32) _PR_MD_GET_SOCKET_ERROR(void); diff --git a/pr/include/prnetdb.h b/pr/include/prnetdb.h index 2b67f4c8..4a282b69 100644 --- a/pr/include/prnetdb.h +++ b/pr/include/prnetdb.h @@ -92,6 +92,49 @@ PR_EXTERN(PRStatus) PR_GetHostByName( /*********************************************************************** ** FUNCTION: +** DESCRIPTION: PR_GetIPNodeByName() +** Lookup a host by name. Equivalent to getipnodebyname(AI_DEFAULT) +** of RFC 2553. +** +** INPUTS: +** char *hostname Character string defining the host name of interest +** PRUint16 af Address family (either PR_AF_INET or PR_AF_INET6) +** PRIntn flags Specifies the types of addresses that are searched +** for and the types of addresses that are returned. +** The only supported flag is PR_AI_DEFAULT. +** char *buf A scratch buffer for the runtime to return result. +** This buffer is allocated by the caller. +** PRIntn bufsize Number of bytes in 'buf'. A recommnded value to +** use is PR_NETDB_BUF_SIZE. +** OUTPUTS: +** PRHostEnt *hostentry +** This structure is filled in by the runtime if +** the function returns PR_SUCCESS. This structure +** is allocated by the caller. +** RETURN: +** PRStatus PR_SUCCESS if the lookup succeeds. If it fails +** the result will be PR_FAILURE and the reason +** for the failure can be retrieved by PR_GetError(). +***********************************************************************/ + +/* + * #define PR_AI_ALL 0x08 + * #define PR_AI_V4MAPPED 0x10 + * #define PR_AI_ADDRCONFIG 0x20 + * #define PR_AI_DEFAULT (PR_AI_V4MAPPED | PR_AI_ADDRCONFIG) + */ +#define PR_AI_DEFAULT 0x30 + +PR_EXTERN(PRStatus) PR_GetIPNodeByName( + const char *hostname, + PRUint16 af, + PRIntn flags, + char *buf, + PRIntn bufsize, + PRHostEnt *hostentry); + +/*********************************************************************** +** FUNCTION: ** DESCRIPTION: PR_GetHostByAddr() ** Lookup a host entry by its network address. ** @@ -160,7 +203,7 @@ PR_EXTERN(PRIntn) PR_EnumerateHostEnt( ** special well known values that are equivalent to ** INADDR_ANY and INADDR_LOOPBACK. ** -** PRUInt16 port The port number to be assigned in the structure. +** PRUint16 port The port number to be assigned in the structure. ** ** OUTPUTS: ** PRNetAddr *addr The address to be manipulated. @@ -181,6 +224,80 @@ PR_EXTERN(PRStatus) PR_InitializeNetAddr( PRNetAddrValue val, PRUint16 port, PRNetAddr *addr); /*********************************************************************** +** FUNCTION: PR_SetNetAddr(), +** DESCRIPTION: +** Set the fields of a PRNetAddr, assigning well known values as +** appropriate. This function is similar to PR_InitializeNetAddr +** but differs in that the address family is specified. +** +** INPUTS +** PRNetAddrValue val The value to be assigned to the IP Address portion +** of the network address. This can only specify the +** special well known values that are equivalent to +** INADDR_ANY and INADDR_LOOPBACK. +** +** PRUint16 af The address family (either PR_AF_INET or PR_AF_INET6) +** +** PRUint16 port The port number to be assigned in the structure. +** +** OUTPUTS: +** PRNetAddr *addr The address to be manipulated. +** +** RETURN: +** PRStatus To indicate success or failure. If the latter, the +** reason for the failure can be retrieved by calling +** PR_GetError(); +***********************************************************************/ +PR_EXTERN(PRStatus) PR_SetNetAddr( + PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr); + +/*********************************************************************** +** FUNCTION: +** DESCRIPTION: PR_IsNetAddrType() +** Determine if the network address is of the specified type. +** +** INPUTS: +** const PRNetAddr *addr A network address. +** PRNetAddrValue The type of network address +** +** RETURN: +** PRBool PR_TRUE if the network address is of the +** specified type, else PR_FALSE. +***********************************************************************/ +PR_EXTERN(PRBool) PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val); + +/*********************************************************************** +** MACRO: +** DESCRIPTION: PR_NetAddrFamily() +** Get the 'family' field of a PRNetAddr union. +** +** INPUTS: +** const PRNetAddr *addr A network address. +** +** RETURN: +** PRUint16 The 'family' field of 'addr'. +***********************************************************************/ +#define PR_NetAddrFamily(addr) ((addr)->raw.family) + +/*********************************************************************** +** MACRO: +** DESCRIPTION: PR_NetAddrInetPort() +** Get the 'port' field of a PRNetAddr union. +** +** INPUTS: +** const PRNetAddr *addr A network address. +** +** RETURN: +** PRUint16 The 'port' field of 'addr'. +***********************************************************************/ +#ifdef _PR_INET6 +#define PR_NetAddrInetPort(addr) \ + ((addr)->raw.family == PR_AF_INET6 ? (addr)->ipv6.port : (addr)->inet.port) +#else +#define PR_NetAddrInetPort(addr) ((addr)->inet.port) +#endif + +/*********************************************************************** ** FUNCTION: ** DESCRIPTION: PR_GetProtoByName() ** Lookup a protocol entry based on protocol's name diff --git a/pr/include/prshm.h b/pr/include/prshm.h new file mode 100644 index 00000000..a6d5ff9b --- /dev/null +++ b/pr/include/prshm.h @@ -0,0 +1,269 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* +** prshm.h -- NSPR Shared Memory +** +** NSPR Named Shared Memory API provides a cross-platform named +** shared-memory interface. NSPR Named Shared Memory is modeled on +** similar constructs in Unix and Windows operating systems. Shared +** memory allows multiple processes to access one or more common shared +** memory regions, using it as an inter-process communication channel. +** +** Notes on Platform Independence: +** NSPR Named Shared Memory is built on the native services offered +** by most platforms. The NSPR Named Shared Memory API tries to +** provide a least common denominator interface so that it works +** across all supported platforms. To ensure that it works everywhere, +** some platform considerations must be accomodated and the protocol +** for using NSPR Shared Memory API must be observed. +** +** Protocol: +** Multiple shared memories can be created using NSPR's Shared Memory +** feature. For each named shared memory, as defined by the name +** given in the PR_OpenSharedMemory() call, a protocol for using the +** shared memory API is required to ensure desired behavior. Failing +** to follow the protocol may yield unpredictable results. +** +** PR_OpenSharedMemory() will create the shared memory segment, if it +** does not already exist, or open a connection that the existing +** shared memory segment if it already exists. +** +** PR_AttachSharedMemory() should be called following +** PR_OpenSharedMemory() to map the memory segment to an address in +** the application's address space. +** +** PR_AttachSharedMemory() may be called to re-map a shared memory +** segment after detaching the same PRSharedMemory object. Be +** sure to detach it when done. +** +** PR_DetachSharedMemory() should be called to un-map the shared +** memory segment from the application's address space. +** +** PR_CloseSharedMemory() should be called when no further use of the +** PRSharedMemory object is required within a process. Following a +** call to PR_CloseSharedMemory() the PRSharedMemory object is +** invalid and cannot be reused. +** +** PR_DeleteSharedMemory() should be called before process +** termination. After calling PR_DeleteSharedMemory() any further use +** of the shared memory associated with the name may cause +** unpredictable results. +** +** Files: +** The name passed to PR_OpenSharedMemory() should be a valid filename +** for a unix platform. PR_OpenSharedMemory() creates file using the +** name passed in. Some platforms may mangle the name before creating +** the file and the shared memory. +** +** The unix implementation may use SysV IPC shared memory, Posix +** shared memory, or memory mapped files; the filename may used to +** define the namespace. On Windows, the name is significant, but +** there is no file associated with name. +** +** No assumptions about the persistence of data in the named file +** should be made. Depending on platform, the shared memory may be +** mapped onto system paging space and be discarded at process +** termination. +** +** All names provided to PR_OpenSharedMemory() should be valid +** filename syntax or name syntax for shared memory for the target +** platform. Referenced directories should have permissions +** appropriate for writing. +** +** Limits: +** Different platforms have limits on both the number and size of +** shared memory resources. The default system limits on some +** platforms may be smaller than your requirements. These limits may +** be adjusted on some platforms either via boot-time options or by +** setting the size of the system paging space to accomodate more +** and/or larger shared memory segment(s). +** +** Security: +** On unix platforms, depending on implementation, contents of the +** backing store for the shared memory can be exposed via the file +** system. Set permissions and or access controls at create and attach +** time to ensure you get the desired security. +** +** On windows platforms, no special security measures are provided. +** +** Example: +** The test case pr/tests/nameshm1.c provides an example of use as +** well as testing the operation of NSPR's Named Shared Memory. +** +** lth. 18-Aug-1999. +*/ + +#ifndef prshm_h___ +#define prshm_h___ + +#include "prtypes.h" + +PR_BEGIN_EXTERN_C + +/* +** Declare opaque type PRSharedMemory. +*/ +typedef struct PRSharedMemory PRSharedMemory; + +/* +** FUNCTION: PR_OpenSharedMemory() +** +** DESCRIPTION: +** PR_OpenSharedMemory() creates a new shared-memory segment or +** associates a previously created memory segment with name. +** +** When parameter create is (PR_SHM_EXCL | PR_SHM_CREATE) and the +** shared memory already exists, the function returns NULL with the +** error set to PR_FILE_EXISTS_ERROR. +** +** When parameter create is PR_SHM_CREATE and the shared memory +** already exists, a handle to that memory segment is returned. If +** the segment does not exist, it is created and a pointer to the +** related PRSharedMemory structure is returned. +** +** When parameter create is 0, and the shared memory exists, a +** pointer to a PRSharedMemory is returned. If the shared memory does +** not exist, NULL is returned with the error set to +** PR_FILE_NOT_FOUND_ERROR. +** +** INPUTS: +** name -- the name the shared-memory segment is known as. +** size -- the size of the shared memory segment. +** flags -- Options for creating the shared memory +** mode -- Same as is passed to PR_Open() +** +** OUTPUTS: +** The shared memory is allocated. +** +** RETURNS: Pointer to opaque structure PRSharedMemory or NULL. +** NULL is returned on error. The reason for the error can be +** retrieved via PR_GetError() and PR_GetOSError(); +** +*/ +PR_EXTERN( PRSharedMemory * ) + PR_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +); +/* Define values for PR_OpenShareMemory(...,create) */ +#define PR_SHM_CREATE 0x1 /* create if not exist */ +#define PR_SHM_EXCL 0x2 /* fail if already exists */ + +/* +** FUNCTION: PR_AttachSharedMemory() +** +** DESCRIPTION: +** PR_AttachSharedMemory() maps the shared-memory described by +** shm to the current process. +** +** INPUTS: +** shm -- The handle returned from PR_OpenSharedMemory(). +** flags -- options for mapping the shared memory. +** PR_SHM_READONLY causes the memory to be attached +** read-only. +** +** OUTPUTS: +** On success, the shared memory segment represented by shm is mapped +** into the process' address space. +** +** RETURNS: Address where shared memory is mapped, or NULL. +** NULL is returned on error. The reason for the error can be +** retrieved via PR_GetError() and PR_GetOSError(); +** +** +*/ +PR_EXTERN( void * ) + PR_AttachSharedMemory( + PRSharedMemory *shm, + PRIntn flags +); +/* Define values for PR_AttachSharedMemory(...,flags) */ +#define PR_SHM_READONLY 0x01 + +/* +** FUNCTION: PR_DetachSharedMemory() +** +** DESCRIPTION: +** PR_DetachSharedMemory() detaches the shared-memory described +** by shm. +** +** INPUTS: +** shm -- The handle returned from PR_OpenSharedMemory(). +** addr -- The address at which the memory was attached. +** +** OUTPUTS: +** The shared memory mapped to an address via a previous call to +** PR_AttachSharedMemory() is unmapped. +** +** RETURNS: PRStatus +** +*/ +PR_EXTERN( PRStatus ) + PR_DetachSharedMemory( + PRSharedMemory *shm, + void *addr +); + +/* +** FUNCTION: PR_CloseSharedMemory() +** +** DESCRIPTION: +** PR_CloseSharedMemory() closes the shared-memory described by +** shm. +** +** INPUTS: +** shm -- The handle returned from PR_OpenSharedMemory(). +** +** OUTPUTS: +** the shared memory represented by shm is closed +** +** RETURNS: PRStatus +** +*/ +PR_EXTERN( PRStatus ) + PR_CloseSharedMemory( + PRSharedMemory *shm +); + +/* +** FUNCTION: PR_DeleteSharedMemory() +** +** DESCRIPTION: +** The shared memory resource represented by name is released. +** +** INPUTS: +** name -- the name the shared-memory segment +** +** OUTPUTS: +** depending on platform, resources may be returned to the underlying +** operating system. +** +** RETURNS: PRStatus +** +*/ +PR_EXTERN( PRStatus ) + PR_DeleteSharedMemory( + const char *name +); + +PR_END_EXTERN_C + +#endif /* prshm_h___ */ diff --git a/pr/include/prshma.h b/pr/include/prshma.h new file mode 100644 index 00000000..55c0d396 --- /dev/null +++ b/pr/include/prshma.h @@ -0,0 +1,252 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* +** prshma.h -- NSPR Anonymous Shared Memory +** +** NSPR provides an anonymous shared memory based on NSPR's PRFileMap +** type. The anonymous file-mapped shared memory provides an inheritable +** shared memory, as in: the child process inherits the shared memory. +** Compare the file-mapped anonymous shared memory to to a named shared +** memory described in prshm.h. The intent is to provide a shared +** memory that is accessable only by parent and child processes. ... +** It's a security thing. +** +** Depending on the underlying platform, the file-mapped shared memory +** may be backed by a file. ... surprise! ... On some platforms, no +** real file backs the shared memory. On platforms where the shared +** memory is backed by a file, the file's name in the filesystem is +** visible to other processes for only the duration of the creation of +** the file, hopefully a very short time. This restricts processess +** that do not inherit the shared memory from opening the file and +** reading or writing its contents. Further, when all processes +** using an anonymous shared memory terminate, the backing file is +** deleted. ... If you are not paranoid, you're not paying attention. +** +** The file-mapped shared memory requires a protocol for the parent +** process and child process to share the memory. NSPR provides two +** protocols. Use one or the other; don't mix and match. +** +** In the first protocol, the job of passing the inheritable shared +** memory is done via helper-functions with PR_CreateProcess(). In the +** second protocol, the parent process is responsible for creating the +** child process; the parent and child are mutually responsible for +** passing a FileMap string. NSPR provides helper functions for +** extracting data from the PRFileMap object. ... See the examples +** below. +** +** Both sides should adhere strictly to the protocol for proper +** operation. The pseudo-code below shows the use of a file-mapped +** shared memory by a parent and child processes. In the examples, the +** server creates the file-mapped shared memory, the client attaches to +** it. +** +** First protocol. +** Server: +** +** fm = PR_OpenAnonFileMap(dirName, size, FilemapProt); +** addr = PR_MemMap(fm); +** attr = PR_NewProcessAttr(); +** PR_ProcessAttrSetInheritableFileMap( attr, fm, shmname ); +** PR_CreateProcess(Client); +** PR_DestroyProcessAttr(attr); +** ... yadda ... +** PR_MemUnmap( addr ); +** PR_CloseFileMap(fm); +** +** +** Client: +** ... started by server via PR_CreateProcess() +** fm = PR_GetInheritedFileMap( shmname ); +** addr = PR_MemMap(fm); +** ... yadda ... +** PR_MemUnmap(addr); +** PR_CloseFileMap(fm); +** +** +** Second Protocol: +** Server: +** +** fm = PR_OpenAnonFileMap(dirName, size, FilemapProt); +** fmstring = PR_ExportFileMapAsString( fm ); +** addr = PR_MemMap(fm); +** ... application specific technique to pass fmstring to child +** ... yadda ... Server uses his own magic to create child +** PR_MemUnmap( addr ); +** PR_CloseFileMap(fm); +** +** +** Client: +** ... started by server via his own magic +** ... application specific technique to find fmstring from parent +** fm = PR_ImportFileMapFromString( fmstring ) +** addr = PR_MemMap(fm); +** ... yadda ... +** PR_MemUnmap(addr); +** PR_CloseFileMap(fm); +** +** +** lth. 2-Jul-1999. +** +** Note: The second protocol was requested by NelsonB (7/1999); this is +** to accomodate servers which already create their own child processes +** using platform native methods. +** +*/ + +#ifndef prshma_h___ +#define prshma_h___ + +#include "prtypes.h" +#include "prio.h" +#include "prproces.h" + +PR_BEGIN_EXTERN_C + +/* +** PR_OpenAnonFileMap() -- Creates an anonymous file-mapped shared memory +** +** Description: +** PR_OpenAnonFileMap() creates an anonymous shared memory. If the +** shared memory already exists, a handle is returned to that shared +** memory object. +** +** On Unix platforms, PR_OpenAnonFileMap() uses 'dirName' as a +** directory name, without the trailing '/', to contain the anonymous +** file. A filename is generated for the name. +** +** On Windows platforms, dirName is ignored. +** +** Inputs: +** dirName -- A directory name to contain the anonymous file. +** size -- The size of the shared memory +** prot -- How the shared memory is mapped. See prio.h +** +** Outputs: +** PRFileMap * +** +** Returns: +** Pointer to PRFileMap or NULL on error. +** +*/ +PR_EXTERN( PRFileMap *) +PR_OpenAnonFileMap( + const char *dirName, + PRSize size, + PRFileMapProtect prot +); + +/* +** PR_ProcessAttrSetInheritableFileMap() -- Prepare FileMap for export +** to my children processes via PR_CreateProcess() +** +** Description: +** PR_ProcessAttrSetInheritableFileMap() connects the PRFileMap to +** PRProcessAttr with shmname. A subsequent call to PR_CreateProcess() +** makes the PRFileMap importable by the child process. +** +** Inputs: +** attr -- PRProcessAttr, used to pass data to PR_CreateProcess() +** fm -- PRFileMap structure to be passed to the child process +** shmname -- The name for the PRFileMap; used by child. +** +** Outputs: +** PRFileMap * +** +** Returns: +** PRStatus +** +*/ +PR_EXTERN(PRStatus) +PR_ProcessAttrSetInheritableFileMap( + PRProcessAttr *attr, + PRFileMap *fm, + const char *shmname +); + +/* +** PR_GetInheritedFileMap() -- Import a PRFileMap previously exported +** by my parent process via PR_CreateProcess() +** +** Description: +** PR_GetInheritedFileMap() retrieves a PRFileMap object exported from +** its parent process via PR_CreateProcess(). +** +** Inputs: +** shmname -- The name provided to PR_ProcessAttrSetInheritableFileMap() +** +** Outputs: +** PRFileMap * +** +** Returns: +** PRFileMap pointer or NULL. +** +*/ +PR_EXTERN( PRFileMap *) +PR_GetInheritedFileMap( + const char *shmname +); + +/* +** PR_ExportFileMapAsString() -- Creates a string identifying a PRFileMap +** +** Description: +** Creates an identifier, as a string, from a PRFileMap object +** previously created with PR_OpenAnonFileMap(). +** +** Inputs: +** fm -- PRFileMap pointer to be represented as a string. +** bufsize -- sizeof(buf) +** buf -- a buffer of length PR_FILEMAP_STRING_BUFSIZE +** +** Outputs: +** buf contains the stringized PRFileMap identifier +** +** Returns: +** PRStatus +** +*/ +PR_EXTERN( PRStatus ) +PR_ExportFileMapAsString( + PRFileMap *fm, + PRSize bufsize, + char *buf +); +#define PR_FILEMAP_STRING_BUFSIZE 128 + +/* +** PR_ImportFileMapFromString() -- Creates a PRFileMap from the identifying string +** +** Description: +** PR_ImportFileMapFromString() creates a PRFileMap object from a +** string previously created by PR_ExportFileMapAsString(). +** +** Inputs: +** fmstring -- string created by PR_ExportFileMapAsString() +** +** Returns: +** PRFileMap pointer or NULL. +** +*/ +PR_EXTERN( PRFileMap * ) +PR_ImportFileMapFromString( + const char *fmstring +); + +PR_END_EXTERN_C +#endif /* prshma_h___ */ diff --git a/pr/src/Makefile b/pr/src/Makefile index 3890b5dd..ff1ee292 100644 --- a/pr/src/Makefile +++ b/pr/src/Makefile @@ -98,9 +98,6 @@ endif ifneq ($(OS_RELEASE),V2.0) OS_LIBS += -lc_r endif -ifeq ($(USE_IPV6), 1) -OS_LIBS += -lip6 -endif endif ifeq ($(OS_ARCH),Linux) @@ -116,7 +113,7 @@ ifeq ($(USE_PTHREADS), 1) ifeq (,$(filter-out B.10.10 B.10.20,$(OS_RELEASE))) OS_LIBS = -ldce else -OS_LIBS = -lpthread +OS_LIBS = -lpthread -lrt endif endif ifeq ($(PTHREADS_USER), 1) @@ -171,6 +168,8 @@ OBJS = \ malloc/$(OBJDIR)/prmalloc.$(OBJ_SUFFIX) \ malloc/$(OBJDIR)/prmem.$(OBJ_SUFFIX) \ md/$(OBJDIR)/prosdep.$(OBJ_SUFFIX) \ + memory/$(OBJDIR)/prshm.$(OBJ_SUFFIX) \ + memory/$(OBJDIR)/prshma.$(OBJ_SUFFIX) \ memory/$(OBJDIR)/prseg.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/pralarm.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/pratom.$(OBJ_SUFFIX) \ @@ -182,6 +181,7 @@ OBJS = \ misc/$(OBJDIR)/prerrortable.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/prinit.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/prinrval.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/pripc.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/prlog2.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/prlong.$(OBJ_SUFFIX) \ misc/$(OBJDIR)/prnetdb.$(OBJ_SUFFIX) \ @@ -202,7 +202,8 @@ OBJS += \ io/$(OBJDIR)/prdir.$(OBJ_SUFFIX) \ io/$(OBJDIR)/prfile.$(OBJ_SUFFIX) \ io/$(OBJDIR)/prio.$(OBJ_SUFFIX) \ - io/$(OBJDIR)/prsocket.$(OBJ_SUFFIX) + io/$(OBJDIR)/prsocket.$(OBJ_SUFFIX) \ + misc/$(OBJDIR)/pripcsem.$(OBJ_SUFFIX) ifndef USE_BTHREADS OBJS += \ @@ -283,7 +284,9 @@ OBJS += md/windows/$(OBJDIR)/w95io.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/ntinrval.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/ntsem.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/win32_errors.$(OBJ_SUFFIX) \ + md/windows/$(OBJDIR)/w32ipcsem.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/w32poll.$(OBJ_SUFFIX) \ + md/windows/$(OBJDIR)/w32shm.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/w95dllmain.$(OBJ_SUFFIX) else ifeq ($(OS_TARGET),OS2) @@ -306,6 +309,8 @@ OBJS += md/windows/$(OBJDIR)/ntdllmn.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/ntinrval.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/ntsem.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/win32_errors.$(OBJ_SUFFIX) \ + md/windows/$(OBJDIR)/w32ipcsem.$(OBJ_SUFFIX) \ + md/windows/$(OBJDIR)/w32shm.$(OBJ_SUFFIX) \ md/windows/$(OBJDIR)/w32poll.$(OBJ_SUFFIX) endif endif diff --git a/pr/src/io/prfdcach.c b/pr/src/io/prfdcach.c index 53f91935..64f164b8 100644 --- a/pr/src/io/prfdcach.c +++ b/pr/src/io/prfdcach.c @@ -116,14 +116,14 @@ finished: fd->dtor = NULL; fd->lower = fd->higher = NULL; fd->identity = PR_NSPR_IO_LAYER; - memset(fd->secret, 0, sizeof(PRFilePrivate)); + memset(fd->secret, 0, PRFILEPRIVATE_SIZE); return fd; allocate: fd = PR_NEW(PRFileDesc); if (NULL != fd) { - fd->secret = PR_NEW(PRFilePrivate); + fd->secret = (PRFilePrivate *) PR_MALLOC(PRFILEPRIVATE_SIZE); if (NULL == fd->secret) PR_DELETE(fd); } if (NULL != fd) goto finished; diff --git a/pr/src/io/prlayer.c b/pr/src/io/prlayer.c index d9f66174..43060295 100644 --- a/pr/src/io/prlayer.c +++ b/pr/src/io/prlayer.c @@ -443,8 +443,8 @@ PR_IMPLEMENT(PRStatus) PR_PushIOLayer( fd->lower = insert; fd->higher = insert->higher; - insert->higher = fd; insert->higher->lower = fd; + insert->higher = fd; } return PR_SUCCESS; diff --git a/pr/src/io/prlog.c b/pr/src/io/prlog.c index 24dbfdde..9f13e44b 100644 --- a/pr/src/io/prlog.c +++ b/pr/src/io/prlog.c @@ -82,7 +82,7 @@ static PRLock *_pr_logLock; /* Macros used to reduce #ifdef pollution */ #if defined(_PR_USE_STDIO_FOR_LOGGING) -#define _PUT_LOG(fd, buf, nb) fputs(buf, fd) +#define _PUT_LOG(fd, buf, nb) {fputs(buf, fd); fflush(fd);} #elif defined(_PR_PTHREADS) #define _PUT_LOG(fd, buf, nb) PR_Write(fd, buf, nb) #elif defined(XP_MAC) @@ -199,19 +199,6 @@ void _PR_InitLog(void) } lm = lm->next; } - if (( PR_FALSE == skip_modcheck) && (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); diff --git a/pr/src/io/prmwait.c b/pr/src/io/prmwait.c index a3bd1576..4843e7ba 100644 --- a/pr/src/io/prmwait.c +++ b/pr/src/io/prmwait.c @@ -176,6 +176,7 @@ static PRStatus TimerInit(void) { goto failed; } + PR_INIT_CLIST(&tm_vars.timer_queue); tm_vars.manager_thread = PR_CreateThread( PR_SYSTEM_THREAD, TimerManager, NULL, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); @@ -183,7 +184,6 @@ static PRStatus TimerInit(void) { goto failed; } - PR_INIT_CLIST(&tm_vars.timer_queue); return PR_SUCCESS; failed: diff --git a/pr/src/io/prpolevt.c b/pr/src/io/prpolevt.c index 5151afd1..589c0a77 100644 --- a/pr/src/io/prpolevt.c +++ b/pr/src/io/prpolevt.c @@ -21,16 +21,99 @@ * * Pollable events * + * Pollable events are implemented using layered I/O. The only + * I/O methods that are implemented for pollable events are poll + * and close. No other methods can be invoked on a pollable + * event. + * + * A pipe or socket pair is created and the pollable event layer + * is pushed onto the read end. A pointer to the write end is + * saved in the PRFilePrivate structure of the pollable event. + * ********************************************************************* */ -#include "primpl.h" +#include "prinit.h" +#include "prio.h" +#include "prmem.h" +#include "prerror.h" +#include "prlog.h" + +/* + * These internal functions are declared in primpl.h, + * but we can't include primpl.h because the definition + * of struct PRFilePrivate in this file (for the pollable + * event layer) will conflict with the definition of + * struct PRFilePrivate in primpl.h (for the NSPR layer). + */ +extern PRIntn _PR_InvalidInt(void); +extern PRInt64 _PR_InvalidInt64(void); +extern PRStatus _PR_InvalidStatus(void); +extern PRFileDesc *_PR_InvalidDesc(void); -typedef struct MyFilePrivate { - PRFilePrivate copy; - PRFileDesc *writeEnd; - PRFilePrivate *oldSecret; -} MyFilePrivate; +/* + * PRFilePrivate structure for the NSPR pollable events layer + */ +struct PRFilePrivate { + PRFileDesc *writeEnd; /* the write end of the pipe/socketpair */ +}; + +static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd); + +static PRInt16 PR_CALLBACK _pr_PolEvtPoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags); + +static PRIOMethods _pr_polevt_methods = { + PR_DESC_LAYERED, + _pr_PolEvtClose, + (PRReadFN)_PR_InvalidInt, + (PRWriteFN)_PR_InvalidInt, + (PRAvailableFN)_PR_InvalidInt, + (PRAvailable64FN)_PR_InvalidInt64, + (PRFsyncFN)_PR_InvalidStatus, + (PRSeekFN)_PR_InvalidInt, + (PRSeek64FN)_PR_InvalidInt64, + (PRFileInfoFN)_PR_InvalidStatus, + (PRFileInfo64FN)_PR_InvalidStatus, + (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, + _pr_PolEvtPoll, + (PRAcceptreadFN)_PR_InvalidInt, + (PRTransmitfileFN)_PR_InvalidInt, + (PRGetsocknameFN)_PR_InvalidStatus, + (PRGetpeernameFN)_PR_InvalidStatus, + (PRGetsockoptFN)_PR_InvalidStatus, + (PRSetsockoptFN)_PR_InvalidStatus, + (PRGetsocketoptionFN)_PR_InvalidStatus, + (PRSetsocketoptionFN)_PR_InvalidStatus +}; + +static PRDescIdentity _pr_polevt_id; +static PRCallOnceType _pr_polevt_once_control; +static PRStatus PR_CALLBACK _pr_PolEvtInit(void); + +static PRInt16 PR_CALLBACK _pr_PolEvtPoll( + PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags) +{ + return (fd->lower->methods->poll)(fd->lower, in_flags, out_flags); +} + +static PRStatus PR_CALLBACK _pr_PolEvtInit(void) +{ + _pr_polevt_id = PR_GetUniqueIdentity("NSPR pollable events"); + if (PR_INVALID_IO_LAYER == _pr_polevt_id) { + return PR_FAILURE; + } + return PR_SUCCESS; +} #if !defined(XP_UNIX) || defined(VMS) #define USE_TCP_SOCKETPAIR @@ -38,11 +121,21 @@ typedef struct MyFilePrivate { PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void) { + PRFileDesc *event; PRFileDesc *fd[2]; /* fd[0] is the read end; fd[1] is the write end */ - MyFilePrivate *secret; - secret = PR_NEW(MyFilePrivate); - if (secret == NULL) { + fd[0] = fd[1] = NULL; + + if (PR_CallOnce(&_pr_polevt_once_control, _pr_PolEvtInit) == PR_FAILURE) { + return NULL; + } + + event = PR_CreateIOLayerStub(_pr_polevt_id, &_pr_polevt_methods); + if (NULL == event) { + goto errorExit; + } + event->secret = PR_NEW(PRFilePrivate); + if (event->secret == NULL) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); goto errorExit; } @@ -57,38 +150,48 @@ PR_IMPLEMENT(PRFileDesc *) PR_NewPollableEvent(void) } #endif - secret->copy = *fd[0]->secret; - secret->oldSecret = fd[0]->secret; - secret->writeEnd = fd[1]; - fd[0]->secret = (PRFilePrivate *) secret; + event->secret->writeEnd = fd[1]; + if (PR_PushIOLayer(fd[0], PR_TOP_IO_LAYER, event) == PR_FAILURE) { + goto errorExit; + } return fd[0]; errorExit: - PR_DELETE(secret); + if (fd[0]) { + PR_Close(fd[0]); + PR_Close(fd[1]); + } + if (event) { + PR_DELETE(event->secret); + event->dtor(event); + } return NULL; } -PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event) +static PRStatus PR_CALLBACK _pr_PolEvtClose(PRFileDesc *fd) { - MyFilePrivate *secret; - - secret = (MyFilePrivate *) event->secret; - event->secret = secret->oldSecret; - PR_Close(event); - PR_Close(secret->writeEnd); - PR_DELETE(secret); + PRFileDesc *event; + + event = PR_PopIOLayer(fd, PR_TOP_IO_LAYER); + PR_ASSERT(NULL == event->higher && NULL == event->lower); + PR_Close(fd); + PR_Close(event->secret->writeEnd); + PR_DELETE(event->secret); + event->dtor(event); return PR_SUCCESS; } +PR_IMPLEMENT(PRStatus) PR_DestroyPollableEvent(PRFileDesc *event) +{ + return PR_Close(event); +} + static const char magicChar = '\x38'; PR_IMPLEMENT(PRStatus) PR_SetPollableEvent(PRFileDesc *event) { - MyFilePrivate *secret; - - secret = (MyFilePrivate *) event->secret; - if (PR_Write(secret->writeEnd, &magicChar, 1) != 1) { + if (PR_Write(event->secret->writeEnd, &magicChar, 1) != 1) { return PR_FAILURE; } return PR_SUCCESS; @@ -102,7 +205,7 @@ PR_IMPLEMENT(PRStatus) PR_WaitForPollableEvent(PRFileDesc *event) PRIntn i; #endif - nBytes = PR_Read(event, buf, sizeof(buf)); + nBytes = PR_Read(event->lower, buf, sizeof(buf)); if (nBytes == -1) { return PR_FAILURE; } diff --git a/pr/src/io/prsocket.c b/pr/src/io/prsocket.c index 2f599d8a..5ebd6248 100644 --- a/pr/src/io/prsocket.c +++ b/pr/src/io/prsocket.c @@ -856,49 +856,17 @@ 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(); + PRSendFileData sfd; - 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; - } - /* The socket must be in blocking mode. */ - if (sd->secret->nonblocking) { - PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); - return -1; - } -#if defined(WINNT) - rv = _PR_MD_TRANSMITFILE( - sd, fd, - headers, hlen, flags, timeout); - if ((rv >= 0) && (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. - */ - 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 */ + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; - return rv; + return(PR_SendFile(sd, &sfd, flags, timeout)); } static PRStatus PR_CALLBACK SocketGetName(PRFileDesc *fd, PRNetAddr *addr) @@ -1245,6 +1213,16 @@ PR_IMPLEMENT(PRFileDesc*) PR_NewUDPSocket(void) return PR_Socket(domain, SOCK_DGRAM, 0); } +PR_IMPLEMENT(PRFileDesc *) PR_OpenTCPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_STREAM, 0); +} + +PR_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_DGRAM, 0); +} + PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *f[]) { #ifdef XP_UNIX @@ -1452,10 +1430,11 @@ PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PRInt32 handle) } /* - * _PR_EmulateTransmitFile + * _PR_EmulateSendFile * - * Send file fd across socket sd. If headers is non-NULL, 'hlen' - * bytes of headers is sent before sending the file. + * Send file sfd->fd across socket sd. The header and trailer buffers + * specified in the 'sfd' argument are sent before and after the file, + * respectively. * * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file * @@ -1463,15 +1442,18 @@ PR_ChangeFileDescNativeHandle(PRFileDesc *fd, PRInt32 handle) * */ -PRInt32 _PR_EmulateTransmitFile(PRFileDesc *sd, PRFileDesc *fd, -const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, -PRIntervalTime timeout) +PRInt32 _PR_EmulateSendFile(PRFileDesc *sd, PRSendFileData *sfd, +PRTransmitFileFlags flags, PRIntervalTime timeout) { PRInt32 rv, count = 0; PRInt32 rlen; + const void *buffer; + PRInt32 buflen; + PRInt32 sendbytes, readbytes; PRThread *me = _PR_MD_CURRENT_THREAD(); char *buf = NULL; -#define _TRANSMITFILE_BUFSIZE (16 * 1024) + +#define _SENDFILE_BUFSIZE (16 * 1024) if (_PR_PENDING_INTERRUPT(me)) { me->flags &= ~_PR_INTERRUPT; @@ -1479,62 +1461,120 @@ PRIntervalTime timeout) return -1; } - buf = (char*)PR_MALLOC(_TRANSMITFILE_BUFSIZE); + buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE); if (buf == NULL) { PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); return -1; } /* - * send headers, first + * send header, first */ - while (hlen) { - rv = PR_Send(sd, headers, hlen, 0, timeout); + buflen = sfd->hlen; + buffer = sfd->header; + while (buflen) { + rv = PR_Send(sd, buffer, buflen, 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; + buffer = (const void*) ((const char*)buffer + rv); + buflen -= 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 (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) { + rv = -1; + goto done; + } + sendbytes = sfd->file_nbytes; + if (sendbytes == 0) { + /* send entire file */ + while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) { + while (rlen) { + char *bufptr = buf; + + rv = PR_Send(sd, bufptr, rlen, 0, 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; + if (rlen < 0) { + /* PR_Read() has invoked PR_SetError(). */ + rv = -1; + goto done; + } } else { - PR_ASSERT(rlen < 0); - /* PR_Read() has invoked PR_SetError(). */ - rv = -1; + readbytes = sendbytes > _SENDFILE_BUFSIZE ? _SENDFILE_BUFSIZE : + sendbytes; + while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) { + while (rlen) { + char *bufptr = buf; + + rv = PR_Send(sd, bufptr, rlen, 0, timeout); + if (rv < 0) { + /* PR_Send() has invoked PR_SetError(). */ + rv = -1; + goto done; + } else { + count += rv; + sendbytes -= rv; + bufptr = ((char*)bufptr + rv); + rlen -= rv; + } + } + readbytes = sendbytes > _SENDFILE_BUFSIZE ? + _SENDFILE_BUFSIZE : sendbytes; + } + if (rlen < 0) { + /* PR_Read() has invoked PR_SetError(). */ + rv = -1; + goto done; + } else if (sendbytes != 0) { + /* + * there are fewer bytes in file to send than specified + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + rv = -1; + goto done; + } } + /* + * send trailer, last + */ + buflen = sfd->tlen; + buffer = sfd->trailer; + while (buflen) { + rv = PR_Send(sd, buffer, buflen, 0, timeout); + if (rv < 0) { + /* PR_Send() has invoked PR_SetError(). */ + rv = -1; + goto done; + } else { + count += rv; + buffer = (const void*) ((const char*)buffer + rv); + buflen -= rv; + } + } + rv = count; done: if (buf) PR_DELETE(buf); + if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) + PR_Close(sd); return rv; } @@ -1830,3 +1870,45 @@ out_of_memory: #endif /* !defined(NEED_SELECT) */ } + +PR_IMPLEMENT(PRInt32) PR_SendFile( + PRFileDesc *sd, PRSendFileData *sfd, + 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; + } + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +#if defined(WINNT) + rv = _PR_MD_SENDFILE(sd, sfd, flags, timeout); + if ((rv >= 0) && (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. + */ + PR_FreeFileDesc(sd); + } +#else +#if defined(XP_UNIX) + rv = _PR_UnixSendFile(sd, sfd, flags, timeout); +#else /* XP_UNIX */ + rv = _PR_EmulateSendFile(sd, sfd, flags, timeout); +#endif /* XP_UNIX */ +#endif /* WINNT */ + + return rv; +} diff --git a/pr/src/md/beos/bnet.c b/pr/src/md/beos/bnet.c index fdc9f18c..cf7ca73a 100644 --- a/pr/src/md/beos/bnet.c +++ b/pr/src/md/beos/bnet.c @@ -577,12 +577,6 @@ _MD_accept_read (PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, } PRInt32 -_MD_transmitfile (PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen, PRInt32 flags, PRIntervalTime timeout) -{ - return PR_NOT_IMPLEMENTED_ERROR; -} - -PRInt32 _MD_socket (int af, int type, int flags) { PRInt32 osfd, err; diff --git a/pr/src/md/unix/Makefile b/pr/src/md/unix/Makefile index b38c9e6a..9a36af8f 100644 --- a/pr/src/md/unix/Makefile +++ b/pr/src/md/unix/Makefile @@ -32,6 +32,7 @@ CSRCS = \ uxproces.c \ uxwrap.c \ uxpoll.c \ + uxshm.c \ $(NULL) PTH_USER_CSRCS = \ @@ -225,6 +226,9 @@ ifeq ($(OS_ARCH),SunOS) ifneq ($(OS_RELEASE),4.1.3_U1) ifneq ($(LOCAL_THREADS_ONLY),1) ASFILES = os_$(OS_ARCH).s + ifneq ($(USE_64),1) + ASFILES += os_$(OS_ARCH)_32.s + endif endif endif endif @@ -288,8 +292,15 @@ $(SHARED_LIBRARY): $(ULTRASPARC_ASOBJS) $(INSTALL) -m 444 $(SHARED_LIBRARY) $(DIST)/lib $(ULTRASPARC_ASOBJS): $(ULTRASPARC_ASFILES) +ifeq ($(USE_64),1) + /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v9 $< +else /usr/ccs/bin/as -o $@ -K PIC -P -D_ASM -D__STDC__=0 -xarch=v8plus $< endif + +clean:: + rm -rf $(ULTRASPARC_ASOBJS) +endif endif endif diff --git a/pr/src/md/unix/aix.c b/pr/src/md/unix/aix.c index 0970ff81..b9409590 100644 --- a/pr/src/md/unix/aix.c +++ b/pr/src/md/unix/aix.c @@ -81,10 +81,12 @@ PRIntervalTime _MD_AixIntervalPerSec(void) #include <dlfcn.h> int (*_PT_aix_yield_fcn)() = NULL; +int _pr_aix_send_file_use_disabled = 0; void _MD_EarlyInit(void) { void *main_app_handle; + char *evp; main_app_handle = dlopen(NULL, RTLD_NOW); PR_ASSERT(NULL != main_app_handle); @@ -96,6 +98,11 @@ void _MD_EarlyInit(void) } dlclose(main_app_handle); + if (evp = getenv("NSPR_AIX_SEND_FILE_USE_DISABLED")) { + if (1 == atoi(evp)) + _pr_aix_send_file_use_disabled = 1; + } + #if defined(AIX_TIMERS) _MD_AixIntervalInit(); #endif diff --git a/pr/src/md/unix/objs.mk b/pr/src/md/unix/objs.mk index 6ca4ca52..951d76b8 100644 --- a/pr/src/md/unix/objs.mk +++ b/pr/src/md/unix/objs.mk @@ -22,6 +22,7 @@ CSRCS = \ unix.c \ unix_errors.c \ uxproces.c \ + uxshm.c \ uxwrap.c \ uxpoll.c \ $(NULL) @@ -217,6 +218,9 @@ ifeq ($(OS_ARCH),SunOS) ifneq ($(OS_RELEASE),4.1.3_U1) ifneq ($(LOCAL_THREADS_ONLY),1) ASFILES = os_$(OS_ARCH).s + ifneq ($(USE_64),1) + ASFILES += os_$(OS_ARCH)_32.s + endif endif endif endif diff --git a/pr/src/md/unix/os_Irix.s b/pr/src/md/unix/os_Irix.s index 3296e419..277a00cd 100644 --- a/pr/src/md/unix/os_Irix.s +++ b/pr/src/md/unix/os_Irix.s @@ -47,6 +47,34 @@ retry_push: sw v0,0(a1) sync sw t0,0(a0) + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop jr ra nop @@ -68,11 +96,13 @@ retry_pop: lw v0,0(a0) li t1,1 beq v0,0,done + nop beq v0,t1,retry_pop nop ll v0,0(a0) beq v0,0,done + nop beq v0,t1,retry_pop nop sc t1,0(a0) @@ -81,6 +111,34 @@ retry_pop: lw t0,0(v0) sw t0,0(a0) done: + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop jr ra nop diff --git a/pr/src/md/unix/os_SunOS.s b/pr/src/md/unix/os_SunOS.s index 0e6dab73..514cd8f5 100644 --- a/pr/src/md/unix/os_SunOS.s +++ b/pr/src/md/unix/os_SunOS.s @@ -49,86 +49,3 @@ _MD_FlushRegisterWindows: ret restore -! ====================================================================== -! -! Atomically add a new element to the top of the stack -! -! usage : PR_StackPush(listp, elementp); -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created. -! -! So, the registers used are: -! %o0 [input] - the address of the stack -! %o1 [input] - the address of the element to be added to the stack -! ----------------------- - - .section ".text" - .global PR_StackPush - -PR_StackPush: - -pulock: ld [%o0],%o3 ! - cmp %o3,-1 ! check if stack is locked - be pulock ! loop, if locked - mov -1,%o3 ! use delay-slot - swap [%o0],%o3 ! atomically lock the stack and get - ! the pointer to stack head - cmp %o3,-1 ! check, if the stack is locked - be pulock ! loop, if so - nop - st %o3,[%o1] - retl ! return back to the caller - st %o1,[%o0] ! - - .size PR_StackPush,(.-PR_StackPush) - - -! end -! ====================================================================== - -! ====================================================================== -! -! Atomically remove the element at the top of the stack -! -! usage : elemep = PR_StackPop(listp); -! -! ----------------------- -! Note on REGISTER USAGE: -! as this is a LEAF procedure, a new stack frame is not created. -! -! So, the registers used are: -! %o0 [input] - the address of the stack -! %o1 [input] - work register (top element) -! ----------------------- - - .section ".text" - .global PR_StackPop - -PR_StackPop: - -polock: ld [%o0],%o1 ! - cmp %o1,-1 ! check if stack is locked - be polock ! loop, if locked - mov -1,%o1 ! use delay-slot - swap [%o0],%o1 ! atomically lock the stack and get - ! the pointer to stack head - cmp %o1,-1 ! check, if the stack is locked - be polock ! loop, if so - nop - tst %o1 ! test for empty stack - be,a empty ! is empty - st %g0,[%o0] - ld [%o1], %o2 ! load the second element - st %o2,[%o0] ! set stack head to second - st %g0,[%o1] ! reset the next pointer; for - ! debugging -empty: - retl ! return back to the caller - mov %o1, %o0 ! return the first element - - .size PR_StackPop,(.-PR_StackPop) - -! end -! ====================================================================== diff --git a/pr/src/md/unix/os_SunOS_32.s b/pr/src/md/unix/os_SunOS_32.s new file mode 100644 index 00000000..96f18103 --- /dev/null +++ b/pr/src/md/unix/os_SunOS_32.s @@ -0,0 +1,101 @@ +/* -*- 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.1 (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. + */ + +! ====================================================================== +! +! Atomically add a new element to the top of the stack +! +! usage : PR_StackPush(listp, elementp); +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created. +! +! So, the registers used are: +! %o0 [input] - the address of the stack +! %o1 [input] - the address of the element to be added to the stack +! ----------------------- + + .section ".text" + .global PR_StackPush + +PR_StackPush: + +pulock: ld [%o0],%o3 ! + cmp %o3,-1 ! check if stack is locked + be pulock ! loop, if locked + mov -1,%o3 ! use delay-slot + swap [%o0],%o3 ! atomically lock the stack and get + ! the pointer to stack head + cmp %o3,-1 ! check, if the stack is locked + be pulock ! loop, if so + nop + st %o3,[%o1] + retl ! return back to the caller + st %o1,[%o0] ! + + .size PR_StackPush,(.-PR_StackPush) + + +! end +! ====================================================================== + +! ====================================================================== +! +! Atomically remove the element at the top of the stack +! +! usage : elemep = PR_StackPop(listp); +! +! ----------------------- +! Note on REGISTER USAGE: +! as this is a LEAF procedure, a new stack frame is not created. +! +! So, the registers used are: +! %o0 [input] - the address of the stack +! %o1 [input] - work register (top element) +! ----------------------- + + .section ".text" + .global PR_StackPop + +PR_StackPop: + +polock: ld [%o0],%o1 ! + cmp %o1,-1 ! check if stack is locked + be polock ! loop, if locked + mov -1,%o1 ! use delay-slot + swap [%o0],%o1 ! atomically lock the stack and get + ! the pointer to stack head + cmp %o1,-1 ! check, if the stack is locked + be polock ! loop, if so + nop + tst %o1 ! test for empty stack + be,a empty ! is empty + st %g0,[%o0] + ld [%o1], %o2 ! load the second element + st %o2,[%o0] ! set stack head to second + st %g0,[%o1] ! reset the next pointer; for + ! debugging +empty: + retl ! return back to the caller + mov %o1, %o0 ! return the first element + + .size PR_StackPop,(.-PR_StackPop) + +! end +! ====================================================================== diff --git a/pr/src/md/unix/unix.c b/pr/src/md/unix/unix.c index 4eb75d1b..fd871585 100644 --- a/pr/src/md/unix/unix.c +++ b/pr/src/md/unix/unix.c @@ -2139,7 +2139,20 @@ void _MD_UnblockClockInterrupts() void _MD_InitFileDesc(PRFileDesc *fd) { /* By default, a Unix fd is not closed on exec. */ - PR_ASSERT(0 == fcntl(fd->secret->md.osfd, F_GETFD, 0)); +#ifdef DEBUG + { + int flags; + + /* + * Ignore EBADF error on fd's 0, 1, 2 because they are + * not open in all processes. + */ + flags = fcntl(fd->secret->md.osfd, F_GETFD, 0); + PR_ASSERT((0 == flags) || (-1 == flags + && (0 <= fd->secret->md.osfd && fd->secret->md.osfd <= 2) + && errno == EBADF)); + } +#endif fd->secret->inheritable = PR_TRUE; } @@ -2918,107 +2931,160 @@ PRIntervalTime _PR_UNIX_TicksPerSecond() } /* - * _PR_UnixTransmitFile + * _PR_UnixSendFile * - * Send file fd across socket sd. If headers is non-NULL, 'hlen' - * bytes of headers is sent before sending the file. + * Send file sfd->fd across socket sd. If header/trailer are specified + * they are sent before and after the file, respectively. * * 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) +#define SENDFILE_MMAP_CHUNK (256 * 1024) + +PR_IMPLEMENT(PRInt32) _PR_UnixSendFile(PRFileDesc *sd, +PRSendFileData *sfd, +PRTransmitFileFlags flags, PRIntervalTime timeout) { PRInt32 rv, count = 0; - PRInt32 len, index = 0; + PRInt32 len, file_bytes, index = 0; struct stat statbuf; - struct PRIOVec iov[2]; + struct PRIOVec iov[3]; void *addr; - PRInt32 err; + PRUint32 file_mmap_offset, pagesize; + PRUint32 addr_offset, mmap_len; /* 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; - } + if (fstat(sfd->fd->secret->md.osfd, &statbuf) == -1) { + _PR_MD_MAP_FSTAT_ERROR(_MD_ERRNO()); 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 (sfd->file_nbytes && (statbuf.st_size < + (sfd->file_offset + sfd->file_nbytes))) { + /* + * there are fewer bytes in file to send than specified + */ + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + count = -1; + goto done; + } + if (sfd->file_nbytes) + file_bytes = sfd->file_nbytes; + else + file_bytes = statbuf.st_size - sfd->file_offset; + + pagesize = PR_GetPageSize(); + /* + * If the file is large, mmap and send the file in chunks so as + * to not consume too much virtual address space + */ + if ((sfd->file_offset == 0) || + (sfd->file_offset & (pagesize - 1) == 0)) { + /* + * case 1: page-aligned file offset + */ + mmap_len = file_bytes < SENDFILE_MMAP_CHUNK ? file_bytes : + SENDFILE_MMAP_CHUNK; + + len = mmap_len; + file_mmap_offset = sfd->file_offset; + addr_offset = 0; + } else { + /* + * case 2: non page-aligned file offset + */ + /* find previous page boundary */ + file_mmap_offset = (sfd->file_offset & ~(pagesize - 1)); + + /* number of initial bytes to skip in mmap'd segment */ + addr_offset = sfd->file_offset - file_mmap_offset; + PR_ASSERT(addr_offset > 0); + mmap_len = (file_bytes + addr_offset) < SENDFILE_MMAP_CHUNK ? + (file_bytes + addr_offset) : SENDFILE_MMAP_CHUNK; + len = mmap_len - addr_offset; + } + /* + * Map in (part of) file. Take care of zero-length files. + */ + if (len) { +#ifdef OSF1 + /* + * Use MAP_SHARED to work around a bug in OSF1 that results in + * corrupted data in the memory-mapped region + */ + addr = mmap((caddr_t) 0, mmap_len, PROT_READ, MAP_SHARED, + sfd->fd->secret->md.osfd, file_mmap_offset); +#else + addr = mmap((caddr_t) 0, mmap_len, PROT_READ, MAP_PRIVATE, + sfd->fd->secret->md.osfd, file_mmap_offset); +#endif - 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; - } + if (addr == (void*)-1) { + _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); + count = -1; + goto done; + } + } + /* + * send headers, first, followed by the file + */ + if (sfd->hlen) { + iov[index].iov_base = (char *) sfd->header; + iov[index].iov_len = sfd->hlen; + index++; + } + if (len) { + iov[index].iov_base = (char*)addr + addr_offset; + iov[index].iov_len = len; + index++; + } + if ((file_bytes == len) && (sfd->tlen)) { + /* + * all file data is mapped in; send the trailer too + */ + iov[index].iov_base = (char *) sfd->trailer; + iov[index].iov_len = sfd->tlen; + index++; + } + rv = PR_Writev(sd, iov, index, timeout); + if (len) + munmap(addr,mmap_len); + if (rv >= 0) { + PR_ASSERT((len == file_bytes) || (rv == sfd->hlen + len)); + PR_ASSERT((len != file_bytes) || + (rv == sfd->hlen + len + sfd->tlen)); + file_bytes -= len; + count += rv; + if (0 == file_bytes) /* header, file and trailer are sent */ + goto done; + } 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; + len = file_bytes < SENDFILE_MMAP_CHUNK ? file_bytes : + SENDFILE_MMAP_CHUNK; while (len > 0) { /* * Map in (part of) file */ - PR_ASSERT((count - hlen) % TRANSMITFILE_MMAP_CHUNK == 0); + file_mmap_offset = sfd->file_offset + count - sfd->hlen; + PR_ASSERT((file_mmap_offset % pagesize) == 0); +#ifdef OSF1 + /* + * Use MAP_SHARED to work around a bug in OSF1 that results in + * corrupted data in the memory-mapped region + */ + addr = mmap((caddr_t) 0, len, PROT_READ, MAP_SHARED, + sfd->fd->secret->md.osfd, file_mmap_offset); +#else addr = mmap((caddr_t) 0, len, PROT_READ, MAP_PRIVATE, - fd->secret->md.osfd, count - hlen); + sfd->fd->secret->md.osfd, file_mmap_offset); +#endif if (addr == (void*)-1) { _PR_MD_MAP_MMAP_ERROR(_MD_ERRNO()); @@ -3029,15 +3095,24 @@ PRIntervalTime timeout) munmap(addr,len); if (rv >= 0) { PR_ASSERT(rv == len); - statbuf.st_size -= rv; + file_bytes -= rv; count += rv; - len = statbuf.st_size < TRANSMITFILE_MMAP_CHUNK ? - statbuf.st_size : TRANSMITFILE_MMAP_CHUNK; + len = file_bytes < SENDFILE_MMAP_CHUNK ? + file_bytes : SENDFILE_MMAP_CHUNK; } else { count = -1; goto done; } } + PR_ASSERT(0 == file_bytes); + if (sfd->tlen) { + rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); + if (rv >= 0) { + PR_ASSERT(rv == sfd->tlen); + count += rv; + } else + count = -1; + } done: if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) PR_Close(sd); @@ -3697,6 +3772,14 @@ PRStatus _MD_MemUnmap(void *addr, PRUint32 len) PRStatus _MD_CloseFileMap(PRFileMap *fmap) { + if ( PR_TRUE == fmap->md.isAnonFM ) { + PRStatus rc = PR_Close( fmap->fd ); + if ( PR_FAILURE == rc ) { + PR_LOG( _pr_io_lm, PR_LOG_DEBUG, + ("_MD_CloseFileMap(): error closing anonymnous file map osfd")); + return PR_FAILURE; + } + } PR_DELETE(fmap); return PR_SUCCESS; } diff --git a/pr/src/md/unix/unix_errors.c b/pr/src/md/unix/unix_errors.c index c3321cf3..30ff1f51 100644 --- a/pr/src/md/unix/unix_errors.c +++ b/pr/src/md/unix/unix_errors.c @@ -22,7 +22,7 @@ #endif #include <errno.h> -static void _MD_unix_map_default_error(int err) +void _MD_unix_map_default_error(int err) { PRErrorCode prError; diff --git a/pr/src/md/unix/uxproces.c b/pr/src/md/unix/uxproces.c index acdb3104..8c0a9b96 100644 --- a/pr/src/md/unix/uxproces.c +++ b/pr/src/md/unix/uxproces.c @@ -716,6 +716,7 @@ PRStatus _MD_DetachUnixProcess(PRProcess *process) PR_DELETE(pRec); } } + PR_DELETE(process); done: PR_Unlock(pr_wp.ml); @@ -775,6 +776,7 @@ PRStatus _MD_WaitUnixProcess( } PR_DELETE(pRec); } + PR_DELETE(process); done: PR_Unlock(pr_wp.ml); diff --git a/pr/src/md/unix/uxshm.c b/pr/src/md/unix/uxshm.c new file mode 100644 index 00000000..fe03a192 --- /dev/null +++ b/pr/src/md/unix/uxshm.c @@ -0,0 +1,639 @@ +/* -*- 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.1 (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. + */ + +/* +** uxshm.c -- Unix Implementations NSPR Named Shared Memory +** +** +** lth. Jul-1999. +** +*/ +#include <string.h> +#include <prshm.h> +#include <prerr.h> +#include <prmem.h> +#include "primpl.h" + +extern PRLogModuleInfo *_pr_shm_lm; + + +#define NSPR_IPC_SHM_KEY 'b' +/* +** Implementation for System V +*/ +#if defined PR_HAVE_SYSV_NAMED_SHARED_MEMORY +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define _MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory +#define _MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory +#define _MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory +#define _MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory +#define _MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory + +extern PRSharedMemory * _MD_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +) +{ + PRStatus rc = PR_SUCCESS; + key_t key; + PRSharedMemory *shm; + char ipcname[PR_IPC_NAME_SIZE]; + + rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm ); + if ( PR_FAILURE == rc ) + { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name )); + return( NULL ); + } + + shm = PR_NEWZAP( PRSharedMemory ); + if ( NULL == shm ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory")); + return( NULL ); + } + + shm->ipcname = PR_MALLOC( strlen( ipcname ) + 1 ); + if ( NULL == shm->ipcname ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory")); + return( NULL ); + } + + /* copy args to struct */ + strcpy( shm->ipcname, ipcname ); + shm->size = size; + shm->mode = mode; + shm->flags = flags; + shm->ident = _PR_SHM_IDENT; + + /* create the file first */ + if ( flags & PR_SHM_CREATE ) { + int osfd = open( shm->ipcname, (O_RDWR | O_CREAT), shm->mode ); + if ( -1 == osfd ) { + _PR_MD_MAP_OPEN_ERROR( errno ); + PR_FREEIF( shm->ipcname ); + PR_DELETE( shm ); + return( NULL ); + } + if ( close(osfd == -1 )) { + _PR_MD_MAP_CLOSE_ERROR( errno ); + PR_FREEIF( shm->ipcname ); + PR_DELETE( shm ); + return( NULL ); + } + } + + /* hash the shm.name to an ID */ + key = ftok( shm->ipcname, NSPR_IPC_SHM_KEY ); + if ( -1 == key ) + { + rc = PR_FAILURE; + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): ftok() failed on name: %s", shm->ipcname)); + PR_FREEIF( shm->ipcname ); + PR_DELETE( shm ); + return( NULL ); + } + + /* get the shared memory */ + if ( flags & PR_SHM_CREATE ) { + shm->id = shmget( key, shm->size, ( shm->mode | IPC_CREAT|IPC_EXCL)); + if ( shm->id >= 0 ) { + return( shm ); + } + if ((errno == EEXIST) && (flags & PR_SHM_EXCL)) { + PR_SetError( PR_FILE_EXISTS_ERROR, errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): shmget() exclusive failed, errno: %d", errno)); + PR_FREEIF(shm->ipcname); + PR_DELETE(shm); + return(NULL); + } + } + + shm->id = shmget( key, shm->size, shm->mode ); + if ( -1 == shm->id ) { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): shmget() failed, errno: %d", errno)); + PR_FREEIF(shm->ipcname); + PR_DELETE(shm); + return(NULL); + } + + return( shm ); +} /* end _MD_OpenSharedMemory() */ + +extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags ) +{ + void *addr; + PRUint32 aFlags = shm->mode; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + aFlags |= (flags & PR_SHM_READONLY )? SHM_RDONLY : 0; + + addr = shmat( shm->id, NULL, aFlags ); + if ( (void*)-1 == addr ) + { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_AttachSharedMemory(): shmat() failed on name: %s, OsError: %d", + shm->ipcname, PR_GetOSError() )); + addr = NULL; + } + + return addr; +} + +extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr ) +{ + PRStatus rc = PR_SUCCESS; + PRIntn urc; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + urc = shmdt( addr ); + if ( -1 == urc ) + { + rc = PR_FAILURE; + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DetachSharedMemory(): shmdt() failed on name: %s", shm->ipcname )); + } + + return rc; +} + +extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm ) +{ + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + PR_FREEIF(shm->ipcname); + PR_DELETE(shm); + + return PR_SUCCESS; +} + +extern PRStatus _MD_DeleteSharedMemory( const char *name ) +{ + PRStatus rc = PR_SUCCESS; + key_t key; + int id; + PRIntn urc; + char ipcname[PR_IPC_NAME_SIZE]; + + rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm ); + if ( PR_FAILURE == rc ) + { + PR_SetError( PR_UNKNOWN_ERROR , errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name )); + return(PR_FAILURE); + } + + /* create the file first */ + { + int osfd = open( ipcname, (O_RDWR | O_CREAT), 0666 ); + if ( -1 == osfd ) { + _PR_MD_MAP_OPEN_ERROR( errno ); + return( PR_FAILURE ); + } + if ( close(osfd == -1 )) { + _PR_MD_MAP_CLOSE_ERROR( errno ); + return( PR_FAILURE ); + } + } + + /* hash the shm.name to an ID */ + key = ftok( ipcname, NSPR_IPC_SHM_KEY ); + if ( -1 == key ) + { + rc = PR_FAILURE; + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): ftok() failed on name: %s", ipcname)); + } + + id = shmget( key, 0, 0 ); + if ( -1 == id ) { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): shmget() failed, errno: %d", errno)); + return(PR_FAILURE); + } + + urc = shmctl( id, IPC_RMID, NULL ); + if ( -1 == urc ) + { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): shmctl() failed on name: %s", ipcname )); + return(PR_FAILURE); + } + + urc = unlink( ipcname ); + if ( -1 == urc ) { + _PR_MD_MAP_UNLINK_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): unlink() failed: %s", ipcname )); + return(PR_FAILURE); + } + + return rc; +} /* end _MD_DeleteSharedMemory() */ + +/* +** Implementation for Posix +*/ +#elif defined PR_HAVE_POSIX_NAMED_SHARED_MEMORY +#include <sys/mman.h> +#include <fcntl.h> + +#define _MD_OPEN_SHARED_MEMORY _MD_OpenSharedMemory +#define _MD_ATTACH_SHARED_MEMORY _MD_AttachSharedMemory +#define _MD_DETACH_SHARED_MEMORY _MD_DetachSharedMemory +#define _MD_CLOSE_SHARED_MEMORY _MD_CloseSharedMemory +#define _MD_DELETE_SHARED_MEMORY _MD_DeleteSharedMemory + +struct _MDSharedMemory { + int handle; +}; + +extern PRSharedMemory * _MD_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +) +{ + PRStatus rc = PR_SUCCESS; + PRIntn id; + PRInt32 end; + PRSharedMemory *shm; + char ipcname[PR_IPC_NAME_SIZE]; + + rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm ); + if ( PR_FAILURE == rc ) + { + PR_SetError( PR_UNKNOWN_ERROR , errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name )); + return( NULL ); + } + + shm = PR_NEWZAP( PRSharedMemory ); + if ( NULL == shm ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory")); + return( NULL ); + } + + shm->ipcname = PR_MALLOC( strlen( ipcname ) + 1 ); + if ( NULL == shm->ipcname ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory")); + return( NULL ); + } + + /* copy args to struct */ + strcpy( shm->ipcname, ipcname ); + shm->size = size; + shm->mode = mode; + shm->flags = flags; + shm->ident = _PR_SHM_IDENT; + + /* + ** Create the shared memory + */ + if ( flags & PR_SHM_CREATE ) { + int oflag = (O_CREAT | O_RDWR); + + if ( flags & PR_SHM_EXCL ) + oflag |= O_EXCL; + shm->id = shm_open( shm->ipcname, oflag, shm->mode ); + } else { + shm->id = shm_open( shm->ipcname, O_RDWR, shm->mode ); + } + + if ( -1 == shm->id ) { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): shm_open failed: %s, OSError: %d", + shm->ipcname, PR_GetOSError())); + PR_DELETE( shm->ipcname ); + PR_DELETE( shm ); + return(NULL); + } + + end = ftruncate( shm->id, shm->size ); + if ( -1 == end ) { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): ftruncate failed, OSError: %d", + PR_GetOSError())); + PR_DELETE( shm->ipcname ); + PR_DELETE( shm ); + return(NULL); + } + + return(shm); +} /* end _MD_OpenSharedMemory() */ + +extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags ) +{ + void *addr; + PRIntn prot = (PROT_READ | PROT_WRITE); + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + if ( PR_SHM_READONLY == flags) + prot ^= PROT_WRITE; + + addr = mmap( (void*)0, shm->size, prot, MAP_SHARED, shm->id, 0 ); + if ((void*)-1 == addr ) + { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_AttachSharedMemory(): mmap failed: %s, errno: %d", + shm->ipcname, PR_GetOSError())); + addr = NULL; + } else { + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_AttachSharedMemory(): name: %s, attached at: %p", shm->ipcname, addr)); + } + + return addr; +} + +extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr ) +{ + PRStatus rc = PR_SUCCESS; + PRIntn urc; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + urc = munmap( addr, shm->size ); + if ( -1 == urc ) + { + rc = PR_FAILURE; + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DetachSharedMemory(): munmap failed: %s, errno: %d", + shm->ipcname, PR_GetOSError())); + } + return rc; +} + +extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm ) +{ + int urc; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + urc = close( shm->id ); + if ( -1 == urc ) { + _PR_MD_MAP_CLOSE_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_CloseSharedMemory(): close() failed, error: %d", PR_GetOSError())); + return(PR_FAILURE); + } + PR_DELETE( shm->ipcname ); + PR_DELETE( shm ); + return PR_SUCCESS; +} + +extern PRStatus _MD_DeleteSharedMemory( const char *name ) +{ + PRStatus rc = PR_SUCCESS; + PRUintn urc; + char ipcname[PR_IPC_NAME_SIZE]; + + rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm ); + if ( PR_FAILURE == rc ) + { + PR_SetError( PR_UNKNOWN_ERROR , errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_OpenSharedMemory(): _PR_MakeNativeIPCName() failed: %s", name )); + return( NULL ); + } + + urc = shm_unlink( ipcname ); + if ( -1 == urc ) { + rc = PR_FAILURE; + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): shm_unlink failed: %s, errno: %d", + ipcname, PR_GetOSError())); + } else { + PR_LOG( _pr_shm_lm, PR_LOG_DEBUG, + ("_MD_DeleteSharedMemory(): %s, success", ipcname)); + } + + return rc; +} /* end _MD_DeleteSharedMemory() */ +#endif + + + +/* +** Unix implementation for anonymous memory (file) mapping +*/ +extern PRLogModuleInfo *_pr_shma_lm; + +#include <unistd.h> + +extern PRFileMap* _md_OpenAnonFileMap( + const char *dirName, + PRSize size, + PRFileMapProtect prot +) +{ + PRFileMap *fm = NULL; + PRFileDesc *fd; + int osfd; + PRIntn urc; + PRIntn mode = 0600; + char *genName; + pid_t pid = getpid(); /* for generating filename */ + PRThread *tid = PR_GetCurrentThread(); /* for generating filename */ + int incr; /* for generating filename */ + const int maxTries = 20; /* maximum # attempts at a unique filename */ + + /* + ** generate a filename from input and runtime environment + ** open the file, unlink the file. + ** make maxTries number of attempts at uniqueness in the filename + */ + for ( incr = 0; incr < maxTries ; incr++ ) { + genName = PR_smprintf( "%s/.NSPR-AFM-%d-%p.%d", + dirName, (int) pid, tid, incr ); + if ( NULL == genName ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): PR_snprintf(): failed, generating filename")); + goto Finished; + } + + /* create the file */ + osfd = open( genName, (O_CREAT | O_EXCL | O_RDWR), mode ); + if ( -1 == osfd ) { + if ( EEXIST == errno ) { + PR_smprintf_free( genName ); + continue; /* name exists, try again */ + } else { + _PR_MD_MAP_OPEN_ERROR( errno ); + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): open(): failed, filename: %s, errno: %d", + genName, PR_GetOSError())); + PR_smprintf_free( genName ); + goto Finished; + } + } + break; /* name generation and open successful, break; */ + } /* end for() */ + + if ( incr == maxTries ) { + PR_ASSERT( -1 == osfd ); + PR_ASSERT( EEXIST == errno ); + _PR_MD_MAP_OPEN_ERROR( errno ); + goto Finished; + } + + urc = unlink( genName ); + if ( -1 == urc ) { + _PR_MD_MAP_UNLINK_ERROR( errno ); + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): failed on unlink(), errno: %d", errno)); + PR_smprintf_free( genName ); + close( osfd ); + goto Finished; + } + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): unlink(): %s", genName )); + + PR_smprintf_free( genName ); + + fd = PR_ImportFile( osfd ); + if ( NULL == fd ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): PR_ImportFile(): failed")); + goto Finished; + } + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): fd: %p", fd )); + + urc = ftruncate( fd->secret->md.osfd, size ); + if ( -1 == urc ) { + _PR_MD_MAP_DEFAULT_ERROR( errno ); + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): failed on ftruncate(), errno: %d", errno)); + PR_Close( fd ); + goto Finished; + } + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): ftruncate(): size: %d", size )); + + fm = PR_CreateFileMap( fd, size, prot ); + if ( NULL == fm ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("PR_OpenAnonFileMap(): failed")); + PR_Close( fd ); + goto Finished; + } + fm->md.isAnonFM = PR_TRUE; /* set fd close */ + + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): PR_CreateFileMap(): fm: %p", fm )); + +Finished: + return(fm); +} /* end md_OpenAnonFileMap() */ + +/* +** _md_ExportFileMapAsString() +** +** +*/ +extern PRStatus _md_ExportFileMapAsString( + PRFileMap *fm, + PRSize bufSize, + char *buf +) +{ + PRIntn written; + PRIntn prot = (PRIntn)fm->prot; + + written = PR_snprintf( buf, bufSize, "%ld:%d", + fm->fd->secret->md.osfd, prot ); + + return((written == -1)? PR_FAILURE : PR_SUCCESS); +} /* end _md_ExportFileMapAsString() */ + + +extern PRFileMap * _md_ImportFileMapFromString( + const char *fmstring +) +{ + PRStatus rc; + PRInt32 osfd; + PRIntn prot; /* really: a PRFileMapProtect */ + PRFileDesc *fd; + PRFileMap *fm; + PRFileInfo info; + + PR_sscanf( fmstring, "%ld:%d", &osfd, &prot ); + + /* import the os file descriptor */ + fd = PR_ImportFile( osfd ); + if ( NULL == fd ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): PR_ImportFile() failed")); + goto Finished; + } + + rc = PR_GetOpenFileInfo( fd, &info ); + if ( PR_FAILURE == rc ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): PR_GetOpenFileInfo() failed")); + goto Finished; + } + + fm = PR_CreateFileMap( fd, info.size, (PRFileMapProtect)prot ); + if ( NULL == fm ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): PR_CreateFileMap() failed")); + } + +Finished: + return(fm); +} /* end _md_ImportFileMapFromString() */ diff --git a/pr/src/md/windows/Makefile b/pr/src/md/windows/Makefile index 32000dc9..36744849 100644 --- a/pr/src/md/windows/Makefile +++ b/pr/src/md/windows/Makefile @@ -48,7 +48,9 @@ CSRCS = \ w95cv.c \ w95sock.c \ win32_errors.c \ + w32ipcsem.c \ w32poll.c \ + w32shm.c \ w95dllmain.c \ $(NULL) else @@ -61,7 +63,9 @@ CSRCS = \ ntthread.c \ ntio.c \ win32_errors.c \ + w32ipcsem.c \ w32poll.c \ + w32shm.c \ $(NULL) endif endif diff --git a/pr/src/md/windows/ntio.c b/pr/src/md/windows/ntio.c index 3769d950..47c1efeb 100644 --- a/pr/src/md/windows/ntio.c +++ b/pr/src/md/windows/ntio.c @@ -369,11 +369,20 @@ _PR_MD_PAUSE_CPU(PRIntervalTime ticks) _PR_THREAD_UNLOCK(completed_io); - completed_io->cpu = lockedCPU; + /* + * If an I/O operation is suspended, the thread + * must be running on the same cpu on which the + * I/O operation was issued. + */ + PR_ASSERT(!completed_io->md.thr_bound_cpu || + (completed_io->cpu == completed_io->md.thr_bound_cpu)); + + if (!completed_io->md.thr_bound_cpu) + completed_io->cpu = lockedCPU; completed_io->state = _PR_RUNNABLE; - _PR_RUNQ_LOCK(lockedCPU); - _PR_ADD_RUNQ(completed_io, lockedCPU, pri); - _PR_RUNQ_UNLOCK(lockedCPU); + _PR_RUNQ_LOCK(completed_io->cpu); + _PR_ADD_RUNQ(completed_io, completed_io->cpu, pri); + _PR_RUNQ_UNLOCK(completed_io->cpu); } else { _PR_THREAD_UNLOCK(completed_io); } @@ -382,9 +391,6 @@ _PR_MD_PAUSE_CPU(PRIntervalTime ticks) } } } 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. @@ -393,13 +399,12 @@ _PR_MD_PAUSE_CPU(PRIntervalTime ticks) 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) { + _PR_THREAD_UNLOCK(completed_io); rv = ReleaseSemaphore(completed_io->md.blocked_sema, - 1, &old_count); + 1, NULL); PR_ASSERT(0 != rv); + } else { + _PR_THREAD_UNLOCK(completed_io); } } } @@ -412,11 +417,57 @@ _PR_MD_PAUSE_CPU(PRIntervalTime ticks) return 0; } +static PRStatus +_native_thread_md_wait(PRThread *thread, PRIntervalTime ticks) +{ + DWORD rv; + PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? + INFINITE : PR_IntervalToMilliseconds(ticks); + + /* + * thread waiting for a cvar or a joining thread + */ + rv = WaitForSingleObject(thread->md.blocked_sema, msecs); + switch(rv) { + case WAIT_OBJECT_0: + return PR_SUCCESS; + break; + case WAIT_TIMEOUT: + _PR_THREAD_LOCK(thread); + PR_ASSERT (thread->state != _PR_IO_WAIT); + if (thread->wait.cvar != NULL) { + PR_ASSERT(thread->state == _PR_COND_WAIT); + thread->wait.cvar = NULL; + thread->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(thread); + } else { + /* The CVAR was notified just as the timeout + * occurred. This left the semaphore in the + * signaled state. 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; + } + + return PR_SUCCESS; +} + PRStatus _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) { DWORD rv; + if (_native_threads_only) { + return(_native_thread_md_wait(thread, ticks)); + } if ( thread->flags & _PR_GLOBAL_SCOPE ) { PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? INFINITE : PR_IntervalToMilliseconds(ticks); @@ -429,12 +480,14 @@ _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) _PR_THREAD_LOCK(thread); if (thread->state == _PR_IO_WAIT) { if (thread->io_pending == PR_TRUE) { + thread->state = _PR_RUNNING; 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. + * occurred. This left the semaphore in the signaled + * state. Call WaitForSingleObject() to clear the + * semaphore. */ _PR_THREAD_UNLOCK(thread); rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE); @@ -442,13 +495,15 @@ _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) } } else { if (thread->wait.cvar != NULL) { + PR_ASSERT(thread->state == _PR_COND_WAIT); 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. + * occurred. This left the semaphore in the + * signaled state. Call WaitForSingleObject() + * to clear the semaphore. */ _PR_THREAD_UNLOCK(thread); rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE); @@ -471,11 +526,150 @@ _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks) return PR_SUCCESS; } +static void +_native_thread_io_nowait( + PRThread *thread, + int rv, + int bytes) +{ + int rc; + + PR_ASSERT(rv != 0); + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + PR_ASSERT(thread->io_suspended == PR_FALSE); + PR_ASSERT(thread->io_pending == PR_TRUE); + thread->state = _PR_RUNNING; + thread->io_pending = PR_FALSE; + _PR_THREAD_UNLOCK(thread); + } else { + /* The IO completed just at the same time the + * thread was interrupted. This left the semaphore + * in the signaled state. Call WaitForSingleObject() + * to clear the semaphore. + */ + PR_ASSERT(thread->io_suspended == PR_TRUE); + PR_ASSERT(thread->io_pending == PR_TRUE); + thread->io_pending = PR_FALSE; + _PR_THREAD_UNLOCK(thread); + rc = WaitForSingleObject(thread->md.blocked_sema, INFINITE); + PR_ASSERT(rc == WAIT_OBJECT_0); + } + + thread->md.blocked_io_status = rv; + thread->md.blocked_io_bytes = bytes; + rc = ResetEvent(thread->md.thr_event); + PR_ASSERT(rc != 0); + return; +} + +static PRStatus +_native_thread_io_wait(PRThread *thread, PRIntervalTime ticks) +{ + DWORD rv, bytes; +#define _NATIVE_IO_WAIT_HANDLES 2 +#define _NATIVE_WAKEUP_EVENT_INDEX 0 +#define _NATIVE_IO_EVENT_INDEX 1 + + HANDLE wait_handles[_NATIVE_IO_WAIT_HANDLES]; + + PRUint32 msecs = (ticks == PR_INTERVAL_NO_TIMEOUT) ? + INFINITE : PR_IntervalToMilliseconds(ticks); + + PR_ASSERT(thread->flags & _PR_GLOBAL_SCOPE); + + wait_handles[0] = thread->md.blocked_sema; + wait_handles[1] = thread->md.thr_event; + rv = WaitForMultipleObjects(_NATIVE_IO_WAIT_HANDLES, wait_handles, + FALSE, msecs); + + switch(rv) { + case WAIT_OBJECT_0 + _NATIVE_IO_EVENT_INDEX: + /* + * I/O op completed + */ + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + + PR_ASSERT(thread->io_suspended == PR_FALSE); + PR_ASSERT(thread->io_pending == PR_TRUE); + thread->state = _PR_RUNNING; + thread->io_pending = PR_FALSE; + _PR_THREAD_UNLOCK(thread); + } else { + /* The IO completed just at the same time the + * thread was interrupted. This led to us being + * notified twice. Call WaitForSingleObject() + * to clear the semaphore. + */ + PR_ASSERT(thread->io_suspended == PR_TRUE); + PR_ASSERT(thread->io_pending == PR_TRUE); + thread->io_pending = PR_FALSE; + _PR_THREAD_UNLOCK(thread); + rv = WaitForSingleObject(thread->md.blocked_sema, + INFINITE); + PR_ASSERT(rv == WAIT_OBJECT_0); + } + + rv = GetOverlappedResult((HANDLE) thread->io_fd, + &thread->md.overlapped.overlapped, &bytes, FALSE); + + thread->md.blocked_io_status = rv; + if (rv != 0) { + thread->md.blocked_io_bytes = bytes; + } else { + thread->md.blocked_io_error = GetLastError(); + PR_ASSERT(ERROR_IO_PENDING != thread->md.blocked_io_error); + } + rv = ResetEvent(thread->md.thr_event); + PR_ASSERT(rv != 0); + break; + case WAIT_OBJECT_0 + _NATIVE_WAKEUP_EVENT_INDEX: + /* + * I/O interrupted; + */ +#ifdef DEBUG + _PR_THREAD_LOCK(thread); + PR_ASSERT(thread->io_suspended == PR_TRUE); + _PR_THREAD_UNLOCK(thread); +#endif + break; + case WAIT_TIMEOUT: + _PR_THREAD_LOCK(thread); + if (thread->state == _PR_IO_WAIT) { + thread->state = _PR_RUNNING; + thread->io_suspended = PR_TRUE; + _PR_THREAD_UNLOCK(thread); + } else { + /* + * The thread was interrupted just as the timeout + * occurred. This left the semaphore in the signaled + * state. Call WaitForSingleObject() to clear the + * semaphore. + */ + PR_ASSERT(thread->io_suspended == PR_TRUE); + _PR_THREAD_UNLOCK(thread); + rv = WaitForSingleObject(thread->md.blocked_sema, INFINITE); + PR_ASSERT(rv == WAIT_OBJECT_0); + } + break; + default: + return PR_FAILURE; + break; + } + + return PR_SUCCESS; +} + + static PRStatus _NT_IO_WAIT(PRThread *thread, PRIntervalTime timeout) { PRBool fWait = PR_TRUE; + if (_native_threads_only) { + return(_native_thread_io_wait(thread, timeout)); + } if (!_PR_IS_NATIVE_THREAD(thread)) { _PR_THREAD_LOCK(thread); @@ -510,6 +704,16 @@ void _PR_Unblock_IO_Wait(PRThread *thr) _PRCPU *cpu = thr->cpu; PR_ASSERT(thr->state == _PR_IO_WAIT); + /* + * A thread for which an I/O timed out or was interrupted cannot be + * in an IO_WAIT state except as a result of calling PR_Close or + * PR_NT_CancelIo for the FD. For these two cases, _PR_IO_WAIT state + * is not interruptible + */ + if (thr->md.interrupt_disabled == PR_TRUE) { + _PR_THREAD_UNLOCK(thr); + return; + } thr->io_suspended = PR_TRUE; thr->state = _PR_RUNNABLE; @@ -519,6 +723,11 @@ void _PR_Unblock_IO_Wait(PRThread *thr) _PR_SLEEPQ_LOCK(cpu); _PR_DEL_SLEEPQ(thr, PR_TRUE); _PR_SLEEPQ_UNLOCK(cpu); + /* + * this thread will continue to run on the same cpu until the + * I/O is aborted by closing the FD or calling CancelIO + */ + thr->md.thr_bound_cpu = me->cpu; PR_ASSERT(!(thr->flags & _PR_IDLE_THREAD)); _PR_AddThreadToRunQ(me, thr); @@ -555,8 +764,12 @@ _NT_ResumeIO(PRThread *thread, PRIntervalTime ticks) */ thread->sleep = ticks; - if (fWait) - return _PR_MD_WAIT(thread, ticks); + if (fWait) { + if (!_PR_IS_NATIVE_THREAD(thread)) + return _PR_MD_WAIT(thread, ticks); + else + return _NT_IO_WAIT(thread, ticks); + } return PR_SUCCESS; } @@ -738,13 +951,17 @@ _md_Associate(HANDLE file) { HANDLE port; - port = CreateIoCompletionPort((HANDLE)file, - _pr_completion_port, - KEY_IO, - 0); + if (!_native_threads_only) { + port = CreateIoCompletionPort((HANDLE)file, + _pr_completion_port, + KEY_IO, + 0); - /* XXX should map error codes on failures */ - return (port == _pr_completion_port); + /* XXX should map error codes on failures */ + return (port == _pr_completion_port); + } else { + return 1; + } } /* @@ -1114,10 +1331,21 @@ _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, return -1; memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); - + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; - me->io_fd = osfd; me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = osfd; + rv = AcceptEx((SOCKET)osfd, accept_sock, me->md.acceptex_buf, @@ -1129,13 +1357,24 @@ _PR_MD_FAST_ACCEPT(PRFileDesc *fd, PRNetAddr *raddr, PRUint32 *rlen, if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) { /* Argh! The IO failed */ - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_ACCEPTEX_ERROR(err); return -1; } - if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -1225,10 +1464,21 @@ _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, return -1; memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); - + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; - me->io_fd = sock; me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = sock; + rv = AcceptEx((SOCKET)sock, *newSock, buf, @@ -1239,13 +1489,24 @@ _PR_MD_FAST_ACCEPT_READ(PRFileDesc *sd, PRInt32 *newSock, PRNetAddr **raddr, &(me->md.overlapped.overlapped)); if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING)) { - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_ACCEPTEX_ERROR(err); return -1; } - if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -1343,8 +1604,8 @@ retry: } PRInt32 -_PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRInt32 hlen, - PRInt32 flags, PRIntervalTime timeout) +_PR_MD_SENDFILE(PRFileDesc *sock, PRSendFileData *sfd, + PRInt32 flags, PRIntervalTime timeout) { PRThread *me = _PR_MD_CURRENT_THREAD(); PRInt32 tflags; @@ -1356,7 +1617,7 @@ _PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRI PR_ASSERT(0 != rv); sock->secret->md.io_model_committed = PR_TRUE; } - return _PR_EmulateTransmitFile(sock, file, headers, hlen, flags, timeout); + return _PR_EmulateSendFile(sock, sfd, flags, timeout); } if (me->io_suspended) { @@ -1376,30 +1637,51 @@ _PR_MD_TRANSMITFILE(PRFileDesc *sock, PRFileDesc *file, const void *headers, PRI 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; + me->md.xmit_bufs->Head = (void *)sfd->header; + me->md.xmit_bufs->HeadLength = sfd->hlen; + me->md.xmit_bufs->Tail = (void *)sfd->trailer; + me->md.xmit_bufs->TailLength = sfd->tlen; memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + me->md.overlapped.overlapped.Offset = sfd->file_offset; + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; tflags = 0; if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) tflags = TF_DISCONNECT | TF_REUSE_SOCKET; + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; - me->io_fd = sock->secret->md.osfd; me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = sock->secret->md.osfd; + rv = TransmitFile((SOCKET)sock->secret->md.osfd, - (HANDLE)file->secret->md.osfd, - (DWORD)0, + (HANDLE)sfd->fd->secret->md.osfd, + (DWORD)sfd->file_nbytes, (DWORD)0, (LPOVERLAPPED)&(me->md.overlapped.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_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_TRANSMITFILE_ERROR(err); return -1; } @@ -1465,25 +1747,47 @@ _PR_MD_RECV(PRFileDesc *fd, void *buf, PRInt32 amount, PRIntn flags, } memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; - me->io_fd = osfd; me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = osfd; + rv = ReadFile((HANDLE)osfd, buf, amount, &bytes, &(me->md.overlapped.overlapped)); if ( (rv == 0) && (GetLastError() != ERROR_IO_PENDING) ) { + _PR_THREAD_LOCK(me); me->io_pending = PR_FALSE; me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + if ((err = GetLastError()) == ERROR_HANDLE_EOF) return 0; _PR_MD_MAP_READ_ERROR(err); return -1; } - if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -1542,23 +1846,45 @@ _PR_MD_SEND(PRFileDesc *fd, const void *buf, PRInt32 amount, PRIntn flags, } memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; - me->io_fd = osfd; me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = osfd; + rv = WriteFile((HANDLE)osfd, buf, amount, &bytes, &(me->md.overlapped.overlapped)); if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) { + _PR_THREAD_LOCK(me); me->io_pending = PR_FALSE; me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_WRITE_ERROR(err); return -1; } - if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, timeout) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -1845,7 +2171,6 @@ _PR_MD_OPEN(const char *name, PRIntn osflags, PRIntn mode) return -1; } - /* Note: we didn't bother putting it in nonblocking mode */ return (PRInt32)file; } } @@ -1902,17 +2227,38 @@ _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) fd->secret->md.io_model_committed = PR_TRUE; } - me->io_pending = PR_TRUE; - me->io_fd = f; - me->state = _PR_IO_WAIT; + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = f; + rv = ReadFile((HANDLE)f, (LPVOID)buf, len, &bytes, &me->md.overlapped.overlapped); if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) { - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + if (err == ERROR_HANDLE_EOF) { return 0; } @@ -1920,7 +2266,9 @@ _PR_MD_READ(PRFileDesc *fd, void *buf, PRInt32 len) return -1; } - if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -2020,23 +2368,45 @@ _PR_MD_WRITE(PRFileDesc *fd, void *buf, PRInt32 len) PR_ASSERT(rv != 0); fd->secret->md.io_model_committed = PR_TRUE; } + if (_native_threads_only) + me->md.overlapped.overlapped.hEvent = me->md.thr_event; + + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + me->io_fd = f; - me->io_pending = PR_TRUE; - me->io_fd = f; - me->state = _PR_IO_WAIT; rv = WriteFile((HANDLE)f, buf, len, &bytes, &(me->md.overlapped.overlapped)); if ( (rv == 0) && ((err = GetLastError()) != ERROR_IO_PENDING) ) { - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_WRITE_ERROR(err); return -1; } - if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + if (_native_threads_only && rv) { + _native_thread_io_nowait(me, rv, bytes); + } else if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { PR_ASSERT(0); return -1; } @@ -2192,52 +2562,52 @@ 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(); + _PR_MD_MAP_CLOSE_ERROR(WSAGetLastError()); } else { rv = CloseHandle((HANDLE)osfd)?0:-1; if (rv < 0) - err = GetLastError(); + _PR_MD_MAP_CLOSE_ERROR(GetLastError()); } - if (rv == 0 && me->io_pending) { + if (rv == 0 && me->io_suspended) { 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... + * see if we need to wait or not. */ fWait = me->io_pending; me->io_suspended = PR_FALSE; + me->md.interrupt_disabled = PR_TRUE; _PR_THREAD_UNLOCK(me); if (fWait) _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT); PR_ASSERT(me->io_suspended == PR_FALSE); PR_ASSERT(me->io_pending == PR_FALSE); + /* + * I/O operation is no longer pending; the thread can now + * run on any cpu + */ + _PR_THREAD_LOCK(me); + me->md.interrupt_disabled = PR_FALSE; + me->md.thr_bound_cpu = NULL; me->io_suspended = PR_FALSE; me->io_pending = PR_FALSE; me->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(me); } - } else { - me->io_suspended = PR_FALSE; - if (rv < 0) - _PR_MD_MAP_CLOSE_ERROR(err); } - return rv; } else { if (socket) { rv = closesocket((SOCKET)osfd); @@ -2249,6 +2619,7 @@ _PR_MD_CLOSE(PRInt32 osfd, PRBool socket) _PR_MD_MAP_CLOSE_ERROR(GetLastError()); } } + return rv; } PRStatus @@ -2804,8 +3175,17 @@ _PR_MD_LOCKFILE(PRInt32 f) memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); - me->state = _PR_IO_WAIT; + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + rv = LockFileEx((HANDLE)f, LOCKFILE_EXCLUSIVE_LOCK, 0, @@ -2813,6 +3193,27 @@ _PR_MD_LOCKFILE(PRInt32 f) 0, &me->md.overlapped.overlapped); + if (_native_threads_only) { + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + + if (rv == FALSE) { + err = GetLastError(); + PR_ASSERT(err != ERROR_IO_PENDING); + _PR_MD_MAP_LOCKF_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + } + /* 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. @@ -2835,8 +3236,17 @@ _PR_MD_LOCKFILE(PRInt32 f) */ if ( rv == FALSE && ((err = GetLastError()) != ERROR_IO_PENDING)) { - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + _PR_MD_MAP_LOCKF_ERROR(err); return PR_FAILURE; } @@ -2856,8 +3266,10 @@ _PR_MD_LOCKFILE(PRInt32 f) #endif /* _NEED_351_FILE_LOCKING_HACK */ if (_NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT) == PR_FAILURE) { + _PR_THREAD_LOCK(me); me->io_pending = PR_FALSE; me->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(me); return PR_FAILURE; } @@ -2882,18 +3294,56 @@ _PR_MD_TLOCKFILE(PRInt32 f) memset(&(me->md.overlapped.overlapped), 0, sizeof(OVERLAPPED)); - me->state = _PR_IO_WAIT; + _PR_THREAD_LOCK(me); + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return -1; + } me->io_pending = PR_TRUE; + me->state = _PR_IO_WAIT; + _PR_THREAD_UNLOCK(me); + rv = LockFileEx((HANDLE)f, LOCKFILE_FAIL_IMMEDIATELY|LOCKFILE_EXCLUSIVE_LOCK, 0, 0x7fffffff, 0, &me->md.overlapped.overlapped); + if (_native_threads_only) { + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + + if (rv == FALSE) { + err = GetLastError(); + PR_ASSERT(err != ERROR_IO_PENDING); + _PR_MD_MAP_LOCKF_ERROR(err); + return PR_FAILURE; + } + return PR_SUCCESS; + } 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); + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + + _PR_MD_MAP_LOCKF_ERROR(err); return PR_FAILURE; } #ifdef _NEED_351_FILE_LOCKING_HACK @@ -2903,8 +3353,17 @@ _PR_MD_TLOCKFILE(PRInt32 f) */ if (_nt_version_gets_lockfile_completion == PR_FALSE) { if ( IsFileLocal((HANDLE)f) == _PR_LOCAL_FILE) { - me->io_pending = PR_FALSE; - me->state = _PR_RUNNING; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + return PR_SUCCESS; } } @@ -2912,8 +3371,17 @@ _PR_MD_TLOCKFILE(PRInt32 f) #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; + _PR_THREAD_LOCK(me); + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + if (_PR_PENDING_INTERRUPT(me)) { + me->flags &= ~_PR_INTERRUPT; + PR_SetError(PR_PENDING_INTERRUPT_ERROR, 0); + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; + } + _PR_THREAD_UNLOCK(me); + return PR_FAILURE; } @@ -3180,6 +3648,49 @@ PR_IMPLEMENT(void) PR_NT_UseNonblock() _nt_use_async = 0; } +PR_IMPLEMENT(PRStatus) PR_NT_CancelIo(PRFileDesc *fd) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + PRBool fWait; + PRFileDesc *bottom; + + bottom = PR_GetIdentitiesLayer(fd, PR_NSPR_IO_LAYER); + if (!me->io_suspended || (NULL == bottom) || + (me->io_fd != bottom->secret->md.osfd)) { + PR_SetError(PR_INVALID_STATE_ERROR, 0); + return PR_FAILURE; + } + /* + * The CancelIO operation has to be issued by the same NT thread that + * issued the I/O operation + */ + PR_ASSERT(_PR_IS_NATIVE_THREAD(me) || (me->cpu == me->md.thr_bound_cpu)); + if (me->io_pending) { + if (!CancelIo((HANDLE)bottom->secret->md.osfd)) { + PR_SetError(PR_INVALID_STATE_ERROR, GetLastError()); + return PR_FAILURE; + } + } + _PR_THREAD_LOCK(me); + fWait = me->io_pending; + me->io_suspended = PR_FALSE; + me->state = _PR_IO_WAIT; + me->md.interrupt_disabled = PR_TRUE; + _PR_THREAD_UNLOCK(me); + if (fWait) + _NT_IO_WAIT(me, PR_INTERVAL_NO_TIMEOUT); + me->md.thr_bound_cpu = NULL; + PR_ASSERT(me->io_suspended == PR_FALSE); + PR_ASSERT(me->io_pending == PR_FALSE); + + _PR_THREAD_LOCK(me); + me->md.interrupt_disabled = PR_FALSE; + me->io_suspended = PR_FALSE; + me->io_pending = PR_FALSE; + me->state = _PR_RUNNING; + _PR_THREAD_UNLOCK(me); + return PR_SUCCESS; +} PRInt32 _nt_nonblock_accept(PRFileDesc *fd, struct sockaddr_in *addr, int *len, PRIntervalTime timeout) { diff --git a/pr/src/md/windows/ntmisc.c b/pr/src/md/windows/ntmisc.c index c9aab0e7..e2fb696f 100644 --- a/pr/src/md/windows/ntmisc.c +++ b/pr/src/md/windows/ntmisc.c @@ -563,6 +563,9 @@ PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) { DWORD dwHi, dwLo; DWORD flProtect; + PRUint32 osfd; + + osfd = ( fmap->fd == (PRFileDesc*)-1 )? -1 : fmap->fd->secret->md.osfd; dwLo = (DWORD) (size & 0xffffffff); dwHi = (DWORD) (((PRUint64) size >> 32) & 0xffffffff); @@ -580,7 +583,7 @@ PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) } fmap->md.hFileMap = CreateFileMapping( - (HANDLE) fmap->fd->secret->md.osfd, + (HANDLE) osfd, NULL, flProtect, dwHi, @@ -593,7 +596,8 @@ PRStatus _MD_CreateFileMap(PRFileMap *fmap, PRInt64 size) } return PR_SUCCESS; } - +#include "prlog.h" +extern PRLogModuleInfo *_pr_shma_lm; void * _MD_MemMap( PRFileMap *fmap, PRInt64 offset, @@ -606,6 +610,20 @@ void * _MD_MemMap( dwHi = (DWORD) (((PRUint64) offset >> 32) & 0xffffffff); if ((addr = MapViewOfFile(fmap->md.hFileMap, fmap->md.dwAccess, dwHi, dwLo, len)) == NULL) { + { + LPVOID lpMsgBuf; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ); + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, ("md_memmap(): %s", lpMsgBuf )); + } PR_SetError(PR_UNKNOWN_ERROR, GetLastError()); } return addr; diff --git a/pr/src/md/windows/ntthread.c b/pr/src/md/windows/ntthread.c index cb4f82cf..b6b41ea5 100644 --- a/pr/src/md/windows/ntthread.c +++ b/pr/src/md/windows/ntthread.c @@ -159,6 +159,13 @@ _PR_MD_INIT_THREAD(PRThread *thread) if (thread->md.blocked_sema == NULL) { return PR_FAILURE; } + if (_native_threads_only) { + /* Create the blocking IO semaphore */ + thread->md.thr_event = CreateEvent(NULL, TRUE, FALSE, NULL); + if (thread->md.thr_event == NULL) { + return PR_FAILURE; + } + } } return PR_SUCCESS; @@ -278,6 +285,13 @@ _PR_MD_CLEAN_THREAD(PRThread *thread) PR_ASSERT(rv); thread->md.blocked_sema = 0; } + if (_native_threads_only) { + if (thread->md.thr_event) { + rv = CloseHandle(thread->md.thr_event); + PR_ASSERT(rv); + thread->md.thr_event = 0; + } + } if (thread->md.handle) { rv = CloseHandle(thread->md.handle); @@ -317,6 +331,14 @@ _PR_MD_EXIT_THREAD(PRThread *thread) thread->md.blocked_sema = 0; } + if (_native_threads_only) { + if (thread->md.thr_event) { + rv = CloseHandle(thread->md.thr_event); + PR_ASSERT(rv); + thread->md.thr_event = 0; + } + } + if (thread->md.handle) { rv = CloseHandle(thread->md.handle); PR_ASSERT(rv); @@ -368,7 +390,6 @@ _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; diff --git a/pr/src/md/windows/w32ipcsem.c b/pr/src/md/windows/w32ipcsem.c new file mode 100644 index 00000000..951b1cf8 --- /dev/null +++ b/pr/src/md/windows/w32ipcsem.c @@ -0,0 +1,177 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * File: w32ipcsem.c + * Description: implements named semaphores for NT and WIN95. + */ + +#include "primpl.h" + +#ifndef _PR_GLOBAL_THREADS_ONLY + +/* + * A fiber cannot call WaitForSingleObject because that + * will block the other fibers running on the same thread. + * If a fiber needs to wait on a (semaphore) handle, we + * create a native thread to call WaitForSingleObject and + * have the fiber join the native thread. + */ + +/* + * Arguments, return value, and error code for WaitForSingleObject + */ +struct WaitSingleArg { + HANDLE handle; + DWORD timeout; + DWORD rv; + DWORD error; +}; + +static void WaitSingleThread(void *arg) +{ + struct WaitSingleArg *warg = (struct WaitSingleArg *) arg; + + warg->rv = WaitForSingleObject(warg->handle, warg->timeout); + if (warg->rv == WAIT_FAILED) { + warg->error = GetLastError(); + } +} + +static DWORD FiberSafeWaitForSingleObject( + HANDLE hHandle, + DWORD dwMilliseconds +) +{ + PRThread *me = _PR_MD_CURRENT_THREAD(); + + if (_PR_IS_NATIVE_THREAD(me)) { + return WaitForSingleObject(hHandle, dwMilliseconds); + } else { + PRThread *waitThread; + struct WaitSingleArg warg; + PRStatus rv; + + warg.handle = hHandle; + warg.timeout = dwMilliseconds; + waitThread = PR_CreateThread( + PR_USER_THREAD, WaitSingleThread, &warg, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (waitThread == NULL) { + return WAIT_FAILED; + } + + rv = PR_JoinThread(waitThread); + PR_ASSERT(rv == PR_SUCCESS); + if (rv == PR_FAILURE) { + return WAIT_FAILED; + } + if (warg.rv == WAIT_FAILED) { + SetLastError(warg.error); + } + return warg.rv; + } +} + +#endif /* !_PR_GLOBAL_THREADS_ONLY */ + +PRSem *_PR_MD_OPEN_SEMAPHORE( + const char *osname, PRIntn flags, PRIntn mode, PRUintn value) +{ + PRSem *sem; + + sem = PR_NEW(PRSem); + if (sem == NULL) { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + if (flags & PR_SEM_CREATE) { + sem->sem = CreateSemaphore(NULL, value, 0x7fffffff, osname); + if (sem->sem == NULL) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + PR_DELETE(sem); + return NULL; + } + if ((flags & PR_SEM_EXCL) && (GetLastError() == ERROR_ALREADY_EXISTS)) { + PR_SetError(PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS); + CloseHandle(sem->sem); + PR_DELETE(sem); + return NULL; + } + } else { + sem->sem = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, osname); + if (sem->sem == NULL) { + DWORD err = GetLastError(); + + /* + * If we open a nonexistent named semaphore, NT + * returns ERROR_FILE_NOT_FOUND, while Win95 + * returns ERROR_INVALID_NAME + */ + if (err == ERROR_INVALID_NAME) { + PR_SetError(PR_FILE_NOT_FOUND_ERROR, err); + } else { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + } + PR_DELETE(sem); + return NULL; + } + } + return sem; +} + +PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem) +{ + DWORD rv; + +#ifdef _PR_GLOBAL_THREADS_ONLY + rv = WaitForSingleObject(sem->sem, INFINITE); +#else + rv = FiberSafeWaitForSingleObject(sem->sem, INFINITE); +#endif + PR_ASSERT(rv == WAIT_FAILED || rv == WAIT_OBJECT_0); + if (rv == WAIT_FAILED) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + if (rv != WAIT_OBJECT_0) { + /* Should not happen */ + PR_SetError(PR_UNKNOWN_ERROR, 0); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem) +{ + if (ReleaseSemaphore(sem->sem, 1, NULL) == FALSE) { + _PR_MD_MAP_DEFAULT_ERROR(GetLastError()); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem) +{ + if (CloseHandle(sem->sem) == FALSE) { + _PR_MD_MAP_CLOSE_ERROR(GetLastError()); + return PR_FAILURE; + } + PR_DELETE(sem); + return PR_SUCCESS; +} diff --git a/pr/src/md/windows/w32shm.c b/pr/src/md/windows/w32shm.c new file mode 100644 index 00000000..4fb60e93 --- /dev/null +++ b/pr/src/md/windows/w32shm.c @@ -0,0 +1,309 @@ +/* -*- 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.1 (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 <private/primpl.h> +#include <string.h> +#include <prshm.h> +#include <prerr.h> +#include <prmem.h> + +#if defined(PR_HAVE_WIN32_NAMED_SHARED_MEMORY) + +extern PRLogModuleInfo *_pr_shm_lm; + +extern PRSharedMemory * _MD_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +) +{ + char ipcname[PR_IPC_NAME_SIZE]; + PRStatus rc = PR_SUCCESS; + DWORD dwHi, dwLo; + PRSharedMemory *shm; + DWORD flProtect = ( PAGE_READWRITE ); + + rc = _PR_MakeNativeIPCName( name, ipcname, PR_IPC_NAME_SIZE, _PRIPCShm ); + if ( PR_FAILURE == rc ) + { + PR_SetError(PR_UNKNOWN_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: name is invalid")); + return(NULL); + } + + shm = PR_NEWZAP( PRSharedMemory ); + if ( NULL == shm ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New PRSharedMemory out of memory")); + return(NULL); + } + + shm->ipcname = PR_MALLOC( strlen( ipcname ) + 1 ); + if ( NULL == shm->ipcname ) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0 ); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, ( "PR_OpenSharedMemory: New shm->ipcname out of memory")); + PR_DELETE(shm); + return(NULL); + } + + /* copy args to struct */ + strcpy( shm->ipcname, ipcname ); + shm->size = size; + shm->mode = mode; + shm->flags = flags; + shm->ident = _PR_SHM_IDENT; + + if (flags & PR_SHM_CREATE ) { + /* XXX: Not 64bit safe. Fix when WinNT goes 64bit, if ever */ + dwHi = 0; + dwLo = shm->size; + + shm->handle = CreateFileMapping( + (HANDLE)-1 , + NULL, + flProtect, + dwHi, + dwLo, + shm->ipcname); + + if ( NULL == shm->handle ) { + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: CreateFileMapping() failed: %s", + shm->ipcname )); + PR_SetError( PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS ); + PR_FREEIF( shm->ipcname ) + PR_DELETE( shm ); + return(NULL); + } else { + if (( flags & PR_SHM_EXCL) && ( GetLastError() == ERROR_ALREADY_EXISTS )) { + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: Request exclusive & already exists", + shm->ipcname )); + PR_SetError( PR_FILE_EXISTS_ERROR, ERROR_ALREADY_EXISTS ); + CloseHandle( shm->handle ); + PR_FREEIF( shm->ipcname ) + PR_DELETE( shm ); + return(NULL); + } else { + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: CreateFileMapping() success: %s, handle: %d", + shm->ipcname, shm->handle )); + return(shm); + } + } + } else { + shm->handle = OpenFileMapping( FILE_MAP_ALL_ACCESS, TRUE, shm->ipcname ); + if ( NULL == shm->handle ) { + _PR_MD_MAP_DEFAULT_ERROR( GetLastError()); + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: OpenFileMapping() failed: %s, error: %d", + shm->ipcname, PR_GetOSError())); + PR_FREEIF( shm->ipcname ); + PR_DELETE( shm ); + return(NULL); + } else { + PR_LOG(_pr_shm_lm, PR_LOG_DEBUG, + ( "PR_OpenSharedMemory: OpenFileMapping() success: %s, handle: %d", + shm->ipcname, shm->handle )); + return(shm); + } + } + /* returns from separate paths */ +} + +extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags ) +{ + PRUint32 access = FILE_MAP_WRITE; + void *addr; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + if ( PR_SHM_READONLY & flags ) + access = FILE_MAP_READ; + + addr = MapViewOfFile( shm->handle, + access, + 0, 0, + shm->size ); + + if ( NULL == addr ) { + _PR_MD_MAP_DEFAULT_ERROR( GetLastError()); + PR_LOG( _pr_shm_lm, PR_LOG_ERROR, + ("_MD_AttachSharedMemory: MapViewOfFile() failed. OSerror: %d", PR_GetOSError())); + } + + return( addr ); +} /* end _MD_ATTACH_SHARED_MEMORY() */ + + +extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr ) +{ + PRStatus rc = PR_SUCCESS; + BOOL wrc; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + wrc = UnmapViewOfFile( addr ); + if ( FALSE == wrc ) + { + _PR_MD_MAP_DEFAULT_ERROR( GetLastError()); + PR_LOG( _pr_shm_lm, PR_LOG_ERROR, + ("_MD_DetachSharedMemory: UnmapViewOfFile() failed. OSerror: %d", PR_GetOSError())); + rc = PR_FAILURE; + } + + return( rc ); +} + + +extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm ) +{ + PRStatus rc = PR_SUCCESS; + BOOL wrc; + + PR_ASSERT( shm->ident == _PR_SHM_IDENT ); + + wrc = CloseHandle( shm->handle ); + if ( FALSE == wrc ) + { + _PR_MD_MAP_DEFAULT_ERROR( GetLastError()); + PR_LOG( _pr_shm_lm, PR_LOG_ERROR, + ("_MD_CloseSharedMemory: CloseHandle() failed. OSerror: %d", PR_GetOSError())); + rc = PR_FAILURE; + } + PR_FREEIF( shm->ipcname ); + PR_DELETE( shm ); + + return( rc ); +} /* end _MD_CLOSE_SHARED_MEMORY() */ + +extern PRStatus _MD_DeleteSharedMemory( const char *name ) +{ + return( PR_SUCCESS ); +} + + +/* +** Windows implementation of anonymous memory (file) map +*/ +extern PRLogModuleInfo *_pr_shma_lm; + +extern PRFileMap* _md_OpenAnonFileMap( + const char *dirName, + PRSize size, + PRFileMapProtect prot +) +{ + PRFileMap *fm; + HANDLE hFileMap; + + fm = PR_CreateFileMap( (PRFileDesc*)-1, size, prot ); + if ( NULL == fm ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): PR_CreateFileMap(): failed")); + goto Finished; + } + + /* + ** Make fm->md.hFileMap inheritable. We can't use + ** GetHandleInformation and SetHandleInformation + ** because these two functions fail with + ** ERROR_CALL_NOT_IMPLEMENTED on Win95. + */ + if (DuplicateHandle(GetCurrentProcess(), fm->md.hFileMap, + GetCurrentProcess(), &hFileMap, + 0, TRUE /* inheritable */, + DUPLICATE_SAME_ACCESS) == FALSE) { + PR_SetError( PR_UNKNOWN_ERROR, GetLastError() ); + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_OpenAnonFileMap(): DuplicateHandle(): failed")); + PR_CloseFileMap( fm ); + fm = NULL; + goto Finished; + } + CloseHandle(fm->md.hFileMap); + fm->md.hFileMap = hFileMap; + +Finished: + return(fm); +} /* end md_OpenAnonFileMap() */ + +/* +** _md_ExportFileMapAsString() +** +*/ +extern PRStatus _md_ExportFileMapAsString( + PRFileMap *fm, + PRSize bufSize, + char *buf +) +{ + PRIntn written; + + written = PR_snprintf( buf, bufSize, "%d:%ld:%ld", + (PRIntn)fm->prot, (PRInt32)fm->md.hFileMap, (PRInt32)fm->md.dwAccess ); + /* Watch out on the above snprintf(). Windows HANDLE assumes 32bits; windows calls it void* */ + + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ExportFileMapAsString(): prot: %x, hFileMap: %x, dwAccess: %x", + fm->prot, fm->md.hFileMap, fm->md.dwAccess )); + + return((written == -1)? PR_FAILURE : PR_SUCCESS); +} /* end _md_ExportFileMapAsString() */ + + +/* +** _md_ImportFileMapFromString() +** +*/ +extern PRFileMap * _md_ImportFileMapFromString( + const char *fmstring +) +{ + PRIntn prot; + PRInt32 hFileMap; + PRInt32 dwAccess; + PRFileMap *fm = NULL; + + PR_sscanf( fmstring, "%d:%ld:%ld", &prot, &hFileMap, &dwAccess ); + + fm = PR_NEWZAP(PRFileMap); + if ( NULL == fm ) { + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): PR_NEWZAP(): Failed")); + return(fm); + } + + fm->prot = (PRFileMapProtect)prot; + fm->md.hFileMap = (HANDLE)hFileMap; /* Assumes HANDLE is 32bit */ + fm->md.dwAccess = (DWORD)dwAccess; + fm->fd = (PRFileDesc*)-1; + + PR_LOG( _pr_shma_lm, PR_LOG_DEBUG, + ("_md_ImportFileMapFromString(): fm: %p, prot: %d, hFileMap: %8.8x, dwAccess: %8.8x, fd: %x", + fm, prot, fm->md.hFileMap, fm->md.dwAccess, fm->fd)); + return(fm); +} /* end _md_ImportFileMapFromString() */ + +#else +Error! Why is PR_HAVE_WIN32_NAMED_SHARED_MEMORY not defined? +#endif /* PR_HAVE_WIN32_NAMED_SHARED_MEMORY */ +/* --- end w32shm.c --- */ diff --git a/pr/src/md/windows/win32_errors.c b/pr/src/md/windows/win32_errors.c index 1d5ba6af..0e73c560 100644 --- a/pr/src/md/windows/win32_errors.c +++ b/pr/src/md/windows/win32_errors.c @@ -59,7 +59,7 @@ static void _MD_win32_map_default_errno(PRInt32 err) PR_SetError(prError, err); } -static void _MD_win32_map_default_error(PRInt32 err) +void _MD_win32_map_default_error(PRInt32 err) { PRErrorCode prError; diff --git a/pr/src/memory/Makefile b/pr/src/memory/Makefile index 70326e5a..dde16b2d 100644 --- a/pr/src/memory/Makefile +++ b/pr/src/memory/Makefile @@ -28,7 +28,7 @@ OPTIMIZER = endif endif -CSRCS = prseg.c +CSRCS = prseg.c prshm.c prshma.c TARGETS = $(OBJS) diff --git a/pr/src/memory/prshm.c b/pr/src/memory/prshm.c new file mode 100644 index 00000000..bea72d26 --- /dev/null +++ b/pr/src/memory/prshm.c @@ -0,0 +1,137 @@ +/* -*- 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.1 (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. + */ + +/* +** prshm.c -- NSPR Named Shared Memory +** +** lth. Jul-1999. +*/ +#include <string.h> +#include "primpl.h" + +extern PRLogModuleInfo *_pr_shm_lm; + + +#if defined PR_HAVE_SYSV_NAMED_SHARED_MEMORY +/* SysV implementation is in pr/src/md/unix/uxshm.c */ +#elif defined PR_HAVE_POSIX_NAMED_SHARED_MEMORY +/* Posix implementation is in pr/src/md/unix/uxshm.c */ +#elif defined PR_HAVE_WIN32_NAMED_SHARED_MEMORY +/* Win32 implementation is in pr/src/md/windows/w32shm.c */ +#else +/* +** there is no named_shared_memory +*/ +extern PRSharedMemory* _MD_OpenSharedMemory( const char *name, PRSize size, PRIntn flags, PRIntn mode ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +extern void * _MD_AttachSharedMemory( PRSharedMemory *shm, PRIntn flags ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +extern PRStatus _MD_DetachSharedMemory( PRSharedMemory *shm, void *addr ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +extern PRStatus _MD_CloseSharedMemory( PRSharedMemory *shm ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +extern PRStatus _MD_DeleteSharedMemory( const char *name ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} +#endif /* HAVE_SYSV_NAMED_SHARED_MEMORY */ + +/* +** FUNCTION: PR_OpenSharedMemory() +** +*/ +PR_IMPLEMENT( PRSharedMemory * ) + PR_OpenSharedMemory( + const char *name, + PRSize size, + PRIntn flags, + PRIntn mode +) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return( _PR_MD_OPEN_SHARED_MEMORY( name, size, flags, mode )); +} /* end PR_OpenSharedMemory() */ + +/* +** FUNCTION: PR_AttachSharedMemory() +** +*/ +PR_IMPLEMENT( void * ) + PR_AttachSharedMemory( + PRSharedMemory *shm, + PRIntn flags +) +{ + return( _PR_MD_ATTACH_SHARED_MEMORY( shm, flags )); +} /* end PR_AttachSharedMemory() */ + +/* +** FUNCTION: PR_DetachSharedMemory() +** +*/ +PR_IMPLEMENT( PRStatus ) + PR_DetachSharedMemory( + PRSharedMemory *shm, + void *addr +) +{ + return( _PR_MD_DETACH_SHARED_MEMORY( shm, addr )); +} /* end PR_DetachSharedMemory() */ + +/* +** FUNCTION: PR_CloseSharedMemory() +** +*/ +PR_IMPLEMENT( PRStatus ) + PR_CloseSharedMemory( + PRSharedMemory *shm +) +{ + return( _PR_MD_CLOSE_SHARED_MEMORY( shm )); +} /* end PR_CloseSharedMemory() */ + +/* +** FUNCTION: PR_DeleteSharedMemory() +** +*/ +PR_EXTERN( PRStatus ) + PR_DeleteSharedMemory( + const char *name +) +{ + if (!_pr_initialized) _PR_ImplicitInitialization(); + return(_PR_MD_DELETE_SHARED_MEMORY( name )); +} /* end PR_DestroySharedMemory() */ +/* end prshm.c */ diff --git a/pr/src/memory/prshma.c b/pr/src/memory/prshma.c new file mode 100644 index 00000000..7d0eb6a8 --- /dev/null +++ b/pr/src/memory/prshma.c @@ -0,0 +1,123 @@ +/* -*- 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.1 (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. + */ + +/* +** prshma.h -- NSPR Anonymous Shared Memory +** +** +*/ + +#include "primpl.h" + +extern PRLogModuleInfo *_pr_shma_lm; + +#if defined(XP_UNIX) +/* defined in pr/src/md/unix/uxshm.c */ +#elif defined(WIN32) +/* defined in pr/src/md/windows/w32shm.c */ +#else +extern PRFileMap * _PR_MD_OPEN_ANON_FILE_MAP( const char *dirName, PRSize size, PRFileMapProtect prot ) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} +extern PRStatus _PR_MD_EXPORT_FILE_MAP_AS_STRING(PRFileMap *fm, PRSize bufSize, char *buf) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} +extern PRFileMap * _PR_MD_IMPORT_FILE_MAP_FROM_STRING(const char *fmstring) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} +#endif + +/* +** PR_OpenAnonFileMap() -- Creates an anonymous file-mapped shared memory +** +*/ +PR_IMPLEMENT(PRFileMap*) +PR_OpenAnonFileMap( + const char *dirName, + PRSize size, + PRFileMapProtect prot +) +{ + return(_PR_MD_OPEN_ANON_FILE_MAP( dirName, size, prot )); +} /* end PR_OpenAnonFileMap() */ + +/* +** PR_ProcessAttrSetInheritableFileMap() -- Prepare FileMap for export +** to my children processes via PR_CreateProcess() +** +** +*/ +PR_IMPLEMENT( PRStatus) +PR_ProcessAttrSetInheritableFileMap( + PRProcessAttr *attr, + PRFileMap *fm, + const char *shmname +) +{ + PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); + return( PR_FAILURE); +} /* end PR_ProcessAttrSetInheritableFileMap() */ + +/* +** PR_GetInheritedFileMap() -- Import a PRFileMap previously exported +** by my parent process via PR_CreateProcess() +** +*/ +PR_IMPLEMENT( PRFileMap *) +PR_GetInheritedFileMap( + const char *shmname +) +{ + PRFileMap *fm = NULL; + PR_SetError( PR_NOT_IMPLEMENTED_ERROR, 0 ); + return( fm ); +} /* end PR_GetInhteritedFileMap() */ + +/* +** PR_ExportFileMapAsString() -- Creates a string identifying a PRFileMap +** +*/ +PR_IMPLEMENT( PRStatus ) +PR_ExportFileMapAsString( + PRFileMap *fm, + PRSize bufSize, + char *buf +) +{ + return( _PR_MD_EXPORT_FILE_MAP_AS_STRING( fm, bufSize, buf )); +} /* end PR_ExportFileMapAsString() */ + +/* +** PR_ImportFileMapFromString() -- Creates a PRFileMap from the identifying string +** +** +*/ +PR_IMPLEMENT( PRFileMap * ) +PR_ImportFileMapFromString( + const char *fmstring +) +{ + return( _PR_MD_IMPORT_FILE_MAP_FROM_STRING(fmstring)); +} /* end PR_ImportFileMapFromString() */ +/* end prshma.c */ diff --git a/pr/src/misc/Makefile b/pr/src/misc/Makefile index 5d70797b..963265dd 100644 --- a/pr/src/misc/Makefile +++ b/pr/src/misc/Makefile @@ -39,6 +39,7 @@ CSRCS = \ prerrortable.c \ prinit.c \ prinrval.c \ + pripc.c \ prlog2.c \ prlong.c \ prnetdb.c \ @@ -49,6 +50,12 @@ CSRCS = \ prtrace.c \ $(NULL) +ifndef USE_PTHREADS +CSRCS += \ + pripcsem.c \ + $(NULL) +endif + TARGETS = $(OBJS) INCLUDES = -I$(DIST)/include -I$(MOD_DEPTH)/pr/include -I$(MOD_DEPTH)/pr/include/private diff --git a/pr/src/misc/pralarm.c b/pr/src/misc/pralarm.c index 8d8fef00..3cff68c6 100644 --- a/pr/src/misc/pralarm.c +++ b/pr/src/misc/pralarm.c @@ -71,7 +71,7 @@ static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id) if (id != NULL) /* have to put this id back in */ { PRIntervalTime idDelta = now - id->nextNotify; - timer = &alarm->timers; + timer = alarm->timers.next; do { result = (PRAlarmID*)timer; @@ -81,7 +81,7 @@ static PRAlarmID *pr_getNextAlarm(PRAlarm *alarm, PRAlarmID *id) break; } timer = timer->next; - } while (timer != alarm->timers.next); + } while (timer != &alarm->timers); } result = (PRAlarmID*)(timer = PR_LIST_HEAD(&alarm->timers)); PR_REMOVE_LINK(timer); /* remove it from the list */ diff --git a/pr/src/misc/prinit.c b/pr/src/misc/prinit.c index 556e97a4..f84079f4 100644 --- a/pr/src/misc/prinit.c +++ b/pr/src/misc/prinit.c @@ -29,6 +29,8 @@ PRLogModuleInfo *_pr_linker_lm; PRLogModuleInfo *_pr_sched_lm; PRLogModuleInfo *_pr_thread_lm; PRLogModuleInfo *_pr_gc_lm; +PRLogModuleInfo *_pr_shm_lm; +PRLogModuleInfo *_pr_shma_lm; PRFileDesc *_pr_stdin; PRFileDesc *_pr_stdout; @@ -106,13 +108,14 @@ PR_IMPLEMENT(PRBool) PR_VersionCheck(const char *importedVersion) if (vmajor > PR_VMAJOR) { return PR_FALSE; - } else if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) { + } + if (vmajor == PR_VMAJOR && vminor > PR_VMINOR) { return PR_FALSE; - } else if (vminor == PR_VMINOR && vpatch > PR_VPATCH) { + } + if (vmajor == PR_VMAJOR && vminor == PR_VMINOR && vpatch > PR_VPATCH) { return PR_FALSE; - } else { - return PR_TRUE; } + return PR_TRUE; } /* PR_VersionCheck */ @@ -120,11 +123,24 @@ PR_IMPLEMENT(PRBool) PR_Initialized(void) { return _pr_initialized; } +PRInt32 _native_threads_only = 0; + static void _PR_InitStuff(void) { +#ifdef WINNT + char *envp; +#endif + if (_pr_initialized) return; _pr_initialized = PR_TRUE; +#ifdef WINNT + if (envp = getenv("NSPR_NATIVE_THREADS_ONLY")) { + if (atoi(envp) == 1) + _native_threads_only = 1; + } +#endif + (void) PR_GetPageSize(); @@ -137,6 +153,8 @@ static void _PR_InitStuff(void) _pr_sched_lm = PR_NewLogModule("sched"); _pr_thread_lm = PR_NewLogModule("thread"); _pr_gc_lm = PR_NewLogModule("gc"); + _pr_shm_lm = PR_NewLogModule("shm"); + _pr_shma_lm = PR_NewLogModule("shma"); /* NOTE: These init's cannot depend on _PR_MD_CURRENT_THREAD() */ _PR_MD_EARLY_INIT(); @@ -163,7 +181,7 @@ static void _PR_InitStuff(void) #endif #ifndef _PR_GLOBAL_THREADS_ONLY - _PR_InitCPUs(); + _PR_InitCPUs(); #endif /* diff --git a/pr/src/misc/pripc.c b/pr/src/misc/pripc.c new file mode 100644 index 00000000..fb19ab68 --- /dev/null +++ b/pr/src/misc/pripc.c @@ -0,0 +1,113 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * File: pripc.c + * + * Description: functions for IPC support + */ + +#include "primpl.h" + +#include <string.h> + +/* + * A POSIX IPC name must begin with a '/'. + * A POSIX IPC name on Solaris cannot contain any '/' except + * the required leading '/'. + * A POSIX IPC name on HP-UX and OSF1 must be a valid pathname + * in the file system. + * + * The ftok() function for System V IPC requires a valid pathname + * in the file system. + * + * A Win32 IPC name cannot contain '\'. + */ + +static void _pr_ConvertSemName(char *result) +{ +#ifdef _PR_HAVE_POSIX_SEMAPHORES +#if defined(SOLARIS) + char *p; + + /* Convert '/' to '_' except for the leading '/' */ + for (p = result+1; *p; p++) { + if (*p == '/') { + *p = '_'; + } + } + return; +#else + return; +#endif +#elif defined(_PR_HAVE_SYSV_SEMAPHORES) + return; +#elif defined(WIN32) + return; +#endif +} + +static void _pr_ConvertShmName(char *result) +{ +#if defined(PR_HAVE_POSIX_NAMED_SHARED_MEMORY) +#if defined(SOLARIS) + char *p; + + /* Convert '/' to '_' except for the leading '/' */ + for (p = result+1; *p; p++) { + if (*p == '/') { + *p = '_'; + } + } + return; +#else + return; +#endif +#elif defined(PR_HAVE_SYSV_NAMED_SHARED_MEMORY) + return; +#elif defined(WIN32) + return; +#else + return; +#endif +} + +PRStatus _PR_MakeNativeIPCName( + const char *name, + char *result, + PRIntn size, + _PRIPCType type) +{ + if (strlen(name) >= (PRSize)size) { + PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); + return PR_FAILURE; + } + strcpy(result, name); + switch (type) { + case _PRIPCSem: + _pr_ConvertSemName(result); + break; + case _PRIPCShm: + _pr_ConvertShmName(result); + break; + default: + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + return PR_SUCCESS; +} diff --git a/pr/src/misc/pripcsem.c b/pr/src/misc/pripcsem.c new file mode 100644 index 00000000..7013ec18 --- /dev/null +++ b/pr/src/misc/pripcsem.c @@ -0,0 +1,111 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * File: pripcsem.c + * + * Description: implements the named semaphores API in prsemipc.h + * for classic NSPR. If _PR_HAVE_NAMED_SEMAPHORES is not defined, + * the named semaphore functions all fail with the error code + * PR_NOT_IMPLEMENTED_ERROR. + */ + +#include "primpl.h" + +#ifdef _PR_PTHREADS + +#error "This file should not be compiled for the pthreads version" + +#else + +#ifndef _PR_HAVE_NAMED_SEMAPHORES + +PRSem * _PR_MD_OPEN_SEMAPHORE( + const char *osname, PRIntn flags, PRIntn mode, PRUintn value) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return NULL; +} + +PRStatus _PR_MD_WAIT_SEMAPHORE(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PRStatus _PR_MD_POST_SEMAPHORE(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PRStatus _PR_MD_CLOSE_SEMAPHORE(PRSem *sem) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +PRStatus _PR_MD_DELETE_SEMAPHORE(const char *osname) +{ + PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0); + return PR_FAILURE; +} + +#endif /* !_PR_HAVE_NAMED_SEMAPHORES */ + +PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( + const char *name, PRIntn flags, PRIntn mode, PRUintn value) +{ + char osname[PR_IPC_NAME_SIZE]; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) { + return NULL; + } + return _PR_MD_OPEN_SEMAPHORE(osname, flags, mode, value); +} + +PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) +{ + return _PR_MD_WAIT_SEMAPHORE(sem); +} + +PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) +{ + return _PR_MD_POST_SEMAPHORE(sem); +} + +PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) +{ + return _PR_MD_CLOSE_SEMAPHORE(sem); +} + +PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) +{ + char osname[PR_IPC_NAME_SIZE]; + + if (!_pr_initialized) _PR_ImplicitInitialization(); + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) { + return PR_FAILURE; + } + return _PR_MD_DELETE_SEMAPHORE(osname); +} + +#endif /* _PR_PTHREADS */ diff --git a/pr/src/misc/prnetdb.c b/pr/src/misc/prnetdb.c index 47afc606..de869246 100644 --- a/pr/src/misc/prnetdb.c +++ b/pr/src/misc/prnetdb.c @@ -100,9 +100,6 @@ 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 */ @@ -177,19 +174,68 @@ static char *Alloc(PRIntn amount, char **bufp, PRIntn *buflenp, PRIntn align) return buf; } +#if defined(_PR_INET6) + +typedef enum _PRIPAddrConversion { + _PRIPAddrNoConversion, + _PRIPAddrIPv4Mapped, + _PRIPAddrIPv4Compat +} _PRIPAddrConversion; + +/* +** Convert an IPv4 address (v4) to an IPv4-mapped IPv6 address (v6). +*/ +static void MakeIPv4MappedAddr(const char *v4, char *v6) +{ + memset(v6, 0, 10); + memset(v6 + 10, 0xff, 2); + memcpy(v6 + 12, v4, 4); + PR_ASSERT(IN6_IS_ADDR_V4MAPPED((struct in6_addr *) v6)); +} + +/* +** Convert an IPv4 address (v4) to an IPv4-compatible IPv6 address (v6). +*/ +static void MakeIPv4CompatAddr(const char *v4, char *v6) +{ + memset(v6, 0, 12); + memcpy(v6 + 12, v4, 4); + PR_ASSERT(IN6_IS_ADDR_V4COMPAT((struct in6_addr *) v6)); +} + +#endif /* _PR_INET6 */ + /* ** 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) + struct hostent *from, + char *buf, + PRIntn bufsize, +#if defined(_PR_INET6) + _PRIPAddrConversion conversion, +#endif + PRHostEnt *to) { PRIntn len, na; char **ap; /* Do the easy stuff */ +#if defined(_PR_INET6) + if (conversion != _PRIPAddrNoConversion + && from->h_addrtype == AF_INET) { + PR_ASSERT(from->h_length == 4); + to->h_addrtype = AF_INET6; + to->h_length = 16; + } else { + to->h_addrtype = from->h_addrtype; + to->h_length = from->h_length; + } +#else to->h_addrtype = from->h_addrtype; to->h_length = from->h_length; +#endif /* Copy the official name */ if (!from->h_name) return PR_FAILURE; @@ -223,7 +269,21 @@ static PRStatus CopyHostent( 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; +#if defined(_PR_INET6) + if (conversion != _PRIPAddrNoConversion + && from->h_addrtype == AF_INET) { + if (conversion == _PRIPAddrIPv4Mapped) { + MakeIPv4MappedAddr(*ap, to->h_addr_list[na]); + } else { + PR_ASSERT(conversion == _PRIPAddrIPv4Compat); + MakeIPv4CompatAddr(*ap, to->h_addr_list[na]); + } + } else { + memcpy(to->h_addr_list[na], *ap, to->h_length); + } +#else memcpy(to->h_addr_list[na], *ap, to->h_length); +#endif } to->h_addr_list[na] = 0; return PR_SUCCESS; @@ -277,6 +337,9 @@ PR_IMPLEMENT(PRStatus) PR_GetHostByName( #ifdef XP_UNIX sigset_t oldset; #endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + int error_num; +#endif if (!_pr_initialized) _PR_ImplicitInitialization(); @@ -288,11 +351,17 @@ PR_IMPLEMENT(PRStatus) PR_GetHostByName( #ifdef _PR_INET6 if (_pr_ipv6_enabled) { +#ifdef _PR_HAVE_GETHOSTBYNAME2 h = gethostbyname2(name, AF_INET6); if (NULL == h) { h = gethostbyname2(name, AF_INET); } +#elif defined(_PR_HAVE_GETIPNODEBYNAME) + h = getipnodebyname(name, AF_INET6, AI_DEFAULT, &error_num); +#else +#error "Unknown name-to-address translation function" +#endif } else { @@ -311,12 +380,129 @@ PR_IMPLEMENT(PRStatus) PR_GetHostByName( #endif /* _PR_INET6 */ if (NULL == h) + { +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); +#else PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif + } + else + { +#if defined(_PR_INET6) + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + + if (_pr_ipv6_enabled) conversion = _PRIPAddrIPv4Mapped; + rv = CopyHostent(h, buf, bufsize, conversion, hp); +#else + rv = CopyHostent(h, buf, bufsize, hp); +#endif + if (PR_SUCCESS != rv) + PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + freehostent(h); +#endif + } + UNLOCK_DNS(); +#ifdef XP_UNIX + ENABLECLOCK(&oldset); +#endif + return rv; +} + +PR_IMPLEMENT(PRStatus) PR_GetIPNodeByName( + const char *name, PRUint16 af, PRIntn flags, + char *buf, PRIntn bufsize, PRHostEnt *hp) +{ + struct hostent *h; + PRStatus rv = PR_FAILURE; +#ifdef XP_UNIX + sigset_t oldset; +#endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + int error_num; +#endif + + if (!_pr_initialized) _PR_ImplicitInitialization(); + +#if defined(_PR_INET6) + PR_ASSERT(af == AF_INET || af == AF_INET6); + if (af != AF_INET && af != AF_INET6) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } +#else + PR_ASSERT(af == AF_INET); + if (af != AF_INET) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } +#endif + + /* + * Flags other than PR_AI_DEFAULT are not yet supported. + */ + PR_ASSERT(flags == PR_AI_DEFAULT); + if (flags != PR_AI_DEFAULT) { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return PR_FAILURE; + } + +#ifdef XP_UNIX + DISABLECLOCK(&oldset); +#endif + LOCK_DNS(); + +#ifdef _PR_INET6 +#ifdef _PR_HAVE_GETHOSTBYNAME2 + if (af == AF_INET6) + { + h = gethostbyname2(name, af); + if (NULL == h) + { + h = gethostbyname2(name, AF_INET); + } + } + else + { + h = gethostbyname2(name, af); + } +#elif defined(_PR_HAVE_GETIPNODEBYNAME) + h = getipnodebyname(name, af, AI_DEFAULT, &error_num); +#else +#error "Unknown name-to-address translation function" +#endif +#else /* _PR_INET6 */ +#ifdef XP_OS2_VACPP + h = gethostbyname((char *)name); +#else + h = gethostbyname(name); +#endif +#endif /* _PR_INET6 */ + + if (NULL == h) + { +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); +#else + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif + } else { +#if defined(_PR_INET6) + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + + if (af == AF_INET6) conversion = _PRIPAddrIPv4Mapped; + rv = CopyHostent(h, buf, bufsize, conversion, hp); +#else rv = CopyHostent(h, buf, bufsize, hp); +#endif if (PR_SUCCESS != rv) PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYNAME) + freehostent(h); +#endif } UNLOCK_DNS(); #ifdef XP_UNIX @@ -335,6 +521,9 @@ PR_IMPLEMENT(PRStatus) PR_GetHostByAddr( #ifdef XP_UNIX sigset_t oldset; #endif +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + int error_num; +#endif if (!_pr_initialized) _PR_ImplicitInitialization(); @@ -355,18 +544,44 @@ PR_IMPLEMENT(PRStatus) PR_GetHostByAddr( addr = &hostaddr->inet.ip; addrlen = sizeof(hostaddr->inet.ip); } +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + h = getipnodebyaddr(addr, addrlen, hostaddr->raw.family, &error_num); +#else #ifdef XP_OS2_VACPP h = gethostbyaddr((char *)addr, addrlen, hostaddr->raw.family); #else h = gethostbyaddr(addr, addrlen, hostaddr->raw.family); #endif - if (NULL == h) PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif /* _PR_INET6 && _PR_HAVE_GETIPNODEBYADDR */ + if (NULL == h) + { +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, error_num); +#else + PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, _MD_GETHOST_ERRNO()); +#endif + } else { +#if defined(_PR_INET6) + _PRIPAddrConversion conversion = _PRIPAddrNoConversion; + if (hostaddr->raw.family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr*)addr)) { + conversion = _PRIPAddrIPv4Mapped; + } else if (IN6_IS_ADDR_V4COMPAT((struct in6_addr*)addr)) { + conversion = _PRIPAddrIPv4Compat; + } + } + rv = CopyHostent(h, buf, bufsize, conversion, hostentry); +#else rv = CopyHostent(h, buf, bufsize, hostentry); +#endif if (PR_SUCCESS != rv) { PR_SetError(PR_INSUFFICIENT_RESOURCES_ERROR, 0); } +#if defined(_PR_INET6) && defined(_PR_HAVE_GETIPNODEBYADDR) + freehostent(h); +#endif } UNLOCK_DNS(); #ifdef XP_UNIX @@ -574,11 +789,19 @@ PR_IMPLEMENT(PRUintn) PR_NetAddrSize(const PRNetAddr* addr) { PRUintn addrsize; + /* + * RFC 2553 added a new field (sin6_scope_id) to + * struct sockaddr_in6. PRNetAddr's ipv6 member has a + * scope_id field to match the new field. In order to + * work with older implementations supporting RFC 2133, + * we take the size of struct sockaddr_in6 instead of + * addr->ipv6. + */ if (AF_INET == addr->raw.family) addrsize = sizeof(addr->inet); #if defined(_PR_INET6) else if (AF_INET6 == addr->raw.family) - addrsize = sizeof(addr->ipv6); + addrsize = sizeof(struct sockaddr_in6); #endif #if defined(XP_UNIX) else if (AF_UNIX == addr->raw.family) @@ -597,28 +820,17 @@ PR_IMPLEMENT(PRIntn) PR_EnumerateHostEnt( if (NULL == addr) enumIndex = 0; else { + address->raw.family = hostEnt->h_addrtype; #if defined(_PR_INET6) - if (_pr_ipv6_enabled) + if (AF_INET6 == hostEnt->h_addrtype) { - 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)); - } + memcpy(&address->ipv6.ip, addr, hostEnt->h_length); } 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); } @@ -675,6 +887,82 @@ PR_IMPLEMENT(PRStatus) PR_InitializeNetAddr( return rv; } /* PR_InitializeNetAddr */ +PR_IMPLEMENT(PRStatus) PR_SetNetAddr( + PRNetAddrValue val, PRUint16 af, PRUint16 port, PRNetAddr *addr) +{ + PRStatus rv = PR_SUCCESS; + if (!_pr_initialized) _PR_ImplicitInitialization(); + + addr->raw.family = af; +#if defined(_PR_INET6) + if (af == 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.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_SetNetAddr */ + +PR_IMPLEMENT(PRBool) +PR_IsNetAddrType(const PRNetAddr *addr, PRNetAddrValue val) +{ +#if defined(_PR_INET6) + if (addr->raw.family == AF_INET6) { + if (val == PR_IpAddrAny + && IN6_IS_ADDR_UNSPECIFIED((struct in6_addr*)&addr->ipv6.ip)) { + return PR_TRUE; + } else if (val == PR_IpAddrLoopback + && IN6_IS_ADDR_LOOPBACK((struct in6_addr*)&addr->ipv6.ip)) { + return PR_TRUE; + } + } + else +#endif + { + if (addr->raw.family == AF_INET) { + if (val == PR_IpAddrAny && addr->inet.ip == htonl(INADDR_ANY)) { + return PR_TRUE; + } else if (val == PR_IpAddrLoopback + && addr->inet.ip == htonl(INADDR_LOOPBACK)) { + return PR_TRUE; + } + } + } + return PR_FALSE; +} + PR_IMPLEMENT(PRNetAddr*) PR_CreateNetAddr(PRNetAddrValue val, PRUint16 port) { PRNetAddr *addr = NULL; @@ -700,80 +988,69 @@ PR_IMPLEMENT(PRStatus) PR_DestroyNetAddr(PRNetAddr *addr) 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; + PRIntn rv; + PRStatus status = PR_SUCCESS; #if defined(_PR_INET6) - - if (_pr_ipv6_enabled) + rv = inet_pton(AF_INET6, string, &addr->ipv6.ip); + if (1 == rv) { - /* - ** 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; - } + addr->raw.family = AF_INET6; } else -#endif { - PRUint32 *ip = (PRUint32*)&addr->inet.ip; - -#ifdef XP_OS2_VACPP - *ip = inet_addr((char *)string); -#else - *ip = inet_addr(string); -#endif - if ((PRUint32) -1 == *ip) + PR_ASSERT(0 == rv); + rv = inet_pton(AF_INET, string, &addr->inet.ip); + if (1 == rv) + { + addr->raw.family = AF_INET; + } + else { - /* - * Either the af argument is not AF_INET, or the string argument - * is a malformed address string. - */ + PR_ASSERT(0 == rv); PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); status = PR_FAILURE; } } +#else /* _PR_INET6 */ +#ifdef XP_OS2_VACPP + addr->inet.ip = inet_addr((char *)string); +#else + addr->inet.ip = inet_addr(string); +#endif + if ((PRUint32) -1 == addr->inet.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; + } +#endif /* _PR_INET6 */ - return status; + 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) + if (AF_INET6 == addr->raw.family) { - 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; + if (NULL == inet_ntop(AF_INET6, &addr->ipv6.ip, string, size)) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, errno); + return PR_FAILURE; + } } else #endif /* defined(_PR_INET6) */ { - PR_ASSERT(AF_INET == addr->inet.family); - if (AF_INET != addr->inet.family) goto failed; + PR_ASSERT(AF_INET == addr->raw.family); + PR_ASSERT(size >= 16); + if (size < 16) goto failed; + if (AF_INET != addr->raw.family) goto failed; else { unsigned char *byte = (unsigned char*)&addr->inet.ip; diff --git a/pr/src/nspr.rc b/pr/src/nspr.rc index 7188a8c2..73d704b5 100644 --- a/pr/src/nspr.rc +++ b/pr/src/nspr.rc @@ -45,8 +45,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,2,0,0 - PRODUCTVERSION 3,1,0,0 + FILEVERSION 3,5,0,0 + PRODUCTVERSION 3,5,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -54,7 +54,7 @@ VS_VERSION_INFO VERSIONINFO FILEFLAGS 0x0L #endif FILEOS 0x40004L - FILETYPE 0x1L + FILETYPE 0x2L FILESUBTYPE 0x0L // end win16 @@ -67,7 +67,7 @@ BEGIN BEGIN VALUE "CompanyName", "Netscape Communications Corporation\0" VALUE "FileDescription", "Netscape Portable Run Time\0" - VALUE "FileVersion", "3, 1, 0, 0\0" + VALUE "FileVersion", "3.5\0" #ifdef WINNT VALUE "InternalName", "libnspr3\0" VALUE "OriginalFilename", "libnspr3.dll\0" @@ -77,7 +77,7 @@ BEGIN #endif VALUE "LegalCopyright", "Copyright © 1996\0" VALUE "ProductName", "Netscape Communication Corporation NSPR20\0" - VALUE "ProductVersion", "3, 1, 0, 0\0" + VALUE "ProductVersion", "3.5\0" END END BLOCK "VarFileInfo" diff --git a/pr/src/prvrsion.c b/pr/src/prvrsion.c index b538235f..6c5e59a3 100644 --- a/pr/src/prvrsion.c +++ b/pr/src/prvrsion.c @@ -36,6 +36,11 @@ #if !defined(_PRODUCTION) #define _PRODUCTION "" #endif +#if defined(DEBUG) +#define _DEBUG_STRING " (debug)" +#else +#define _DEBUG_STRING "" +#endif static PRVersionDescription prVersionDescription_libnspr3 = { @@ -65,8 +70,10 @@ static PRVersionDescription prVersionDescription_libnspr3 = /* * Version information for the 'ident' and 'what commands */ -static char rcsid[] = "$Version: NSPR " PR_VERSION " " _BUILD_STRING " $"; -static char sccsid[] = "@(#)NSPR " PR_VERSION " " _BUILD_STRING; +static char rcsid[] = "$Version: NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING " $"; +static char sccsid[] = "@(#)NSPR " PR_VERSION _DEBUG_STRING + " " _BUILD_STRING; #endif /* XP_UNIX */ diff --git a/pr/src/pthreads/ptio.c b/pr/src/pthreads/ptio.c index eabf762a..3b2fa4c8 100644 --- a/pr/src/pthreads/ptio.c +++ b/pr/src/pthreads/ptio.c @@ -19,7 +19,6 @@ /* ** File: ptio.c ** Descritpion: Implemenation of I/O methods for pthreads -** Exports: ptio.h */ #if defined(_PR_PTHREADS) @@ -55,7 +54,7 @@ * The send_file() system call is available in AIX 4.3.2 or later. * If this file is compiled on an older AIX system, it attempts to * look up the send_file symbol at run time to determine whether - * we can use the faster PR_TransmitFile implementation based on + * we can use the faster PR_SendFile/PR_TransmitFile implementation based on * send_file(). On AIX 4.3.2 or later, we can safely skip this * runtime function dispatching and just use the send_file based * implementation. @@ -240,7 +239,11 @@ struct pt_Continuation /* * For sendfile() */ - off_t offset; /* offset in file to send */ + struct file_spec { + off_t offset; /* offset in file to send */ + size_t nbytes; /* length of file data to send */ + size_t st_size; /* file size */ + } file_spec; #endif } arg3; union { PRIntn flags; } arg4; /* #4 - read/write flags */ @@ -269,6 +272,7 @@ struct pt_Continuation PRIntn syserrno; /* in case it failed, why (errno) */ pr_ContuationStatus status; /* the status of the operation */ PRCondVar *complete; /* to notify the initiating thread */ + PRIntn io_tq_index; /* io-queue index */ }; static struct pt_TimedQueue @@ -279,7 +283,14 @@ static struct pt_TimedQueue pt_Continuation *head, *tail; /* head/tail of list of operations */ pt_Continuation *op; /* timed operation furthest in future */ -} pt_tq; + struct pollfd *pollingList; /* list built for polling */ + PRIntn pollingSlotsAllocated; /* # entries available in list */ + pt_Continuation **pollingOps; /* list paralleling polling list */ +} *pt_tqp; /* an array */ + +static PRIntn _pt_num_cpus; +PRIntn _pt_tq_count; /* size of the pt_tqp array */ +static PRInt32 _pt_tq_index; /* atomically incremented */ #if defined(DEBUG) @@ -332,7 +343,7 @@ PR_IMPLEMENT(void) PT_FPrintStats(PRFileDesc *debug_out, const char *msg) /* * The following two functions, pt_InsertTimedInternal and - * pt_FinishTimedInternal, are always called with the pt_tq.ml + * pt_FinishTimedInternal, are always called with the tqp->ml * lock held. The "internal" in the functions' names come from * the Mesa programming language. Internal functions are always * called from inside a monitor. @@ -342,28 +353,34 @@ static void pt_InsertTimedInternal(pt_Continuation *op) { pt_Continuation *t_op = NULL; PRIntervalTime now = PR_IntervalNow(); + struct pt_TimedQueue *tqp = &pt_tqp[op->io_tq_index]; #if defined(DEBUG) { PRIntn count; pt_Continuation *tmp; - PR_ASSERT((pt_tq.head == NULL) == (pt_tq.tail == NULL)); - PR_ASSERT((pt_tq.head == NULL) == (pt_tq.op_count == 0)); - for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; - PR_ASSERT(count == pt_tq.op_count); - for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; - PR_ASSERT(count == pt_tq.op_count); + PRThread *self = PR_GetCurrentThread(); + + PR_ASSERT(tqp == &pt_tqp[self->io_tq_index]); + PR_ASSERT((tqp->head == NULL) == (tqp->tail == NULL)); + PR_ASSERT((tqp->head == NULL) == (tqp->op_count == 0)); + for (tmp = tqp->head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; + PR_ASSERT(count == tqp->op_count); + for (tmp = tqp->tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; + PR_ASSERT(count == tqp->op_count); + for (tmp = tqp->head; tmp != NULL; tmp = tmp->next) + PR_ASSERT(tmp->io_tq_index == op->io_tq_index); } #endif /* defined(DEBUG) */ /* * If this element operation isn't timed, it gets queued at the - * end of the list (just after pt_tq.tail) and we're + * end of the list (just after tqp->tail) and we're * finishd early. */ if (PR_INTERVAL_NO_TIMEOUT == op->timeout) { - t_op = pt_tq.tail; /* put it at the end */ + t_op = tqp->tail; /* put it at the end */ goto done; } @@ -371,7 +388,7 @@ static void pt_InsertTimedInternal(pt_Continuation *op) * 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; + if (NULL == tqp->op) tqp->op = op; else { /* @@ -383,14 +400,14 @@ static void pt_InsertTimedInternal(pt_Continuation *op) * This should be easy! */ - for (t_op = pt_tq.op; NULL != t_op; t_op = t_op->prev) + for (t_op = tqp->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' + * The actual difference 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 @@ -400,7 +417,7 @@ static void pt_InsertTimedInternal(pt_Continuation *op) */ if ((PRInt32)(op->absolute - t_op->absolute) >= 0) { - if (t_op == pt_tq.op) pt_tq.op = op; + if (t_op == tqp->op) tqp->op = op; break; } } @@ -414,11 +431,11 @@ done: * * 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 + * 'op' by updating tqp->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. + * traverse the queue by starting with tqp->head and following + * the 'next' pointers, without having to acquire the tqp->ml lock. * (we do that in pt_ContinuationThreadInternal). We traverse the 'prev' * pointers only in this function, which is called with the lock held. * @@ -428,9 +445,9 @@ done: 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; + op->next = tqp->head; + tqp->head = op; + if (NULL == tqp->tail) tqp->tail = op; else op->next->prev = op; } else @@ -441,35 +458,35 @@ done: op->prev->next = op; if (NULL != op->next) op->next->prev = op; - if (t_op == pt_tq.tail) - pt_tq.tail = op; + if (t_op == tqp->tail) + tqp->tail = op; } - pt_tq.op_count += 1; + tqp->op_count += 1; #if defined(DEBUG) { PRIntn count; pt_Continuation *tmp; - PR_ASSERT(pt_tq.head != NULL); - PR_ASSERT(pt_tq.tail != NULL); - PR_ASSERT(pt_tq.op_count != 0); - PR_ASSERT(pt_tq.head->prev == NULL); - PR_ASSERT(pt_tq.tail->next == NULL); - if (pt_tq.op_count > 1) + PR_ASSERT(tqp->head != NULL); + PR_ASSERT(tqp->tail != NULL); + PR_ASSERT(tqp->op_count != 0); + PR_ASSERT(tqp->head->prev == NULL); + PR_ASSERT(tqp->tail->next == NULL); + if (tqp->op_count > 1) { - PR_ASSERT(pt_tq.head->next != NULL); - PR_ASSERT(pt_tq.tail->prev != NULL); + PR_ASSERT(tqp->head->next != NULL); + PR_ASSERT(tqp->tail->prev != NULL); } else { - PR_ASSERT(pt_tq.head->next == NULL); - PR_ASSERT(pt_tq.tail->prev == NULL); + PR_ASSERT(tqp->head->next == NULL); + PR_ASSERT(tqp->tail->prev == NULL); } - for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; - PR_ASSERT(count == pt_tq.op_count); - for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; - PR_ASSERT(count == pt_tq.op_count); + for (tmp = tqp->head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; + PR_ASSERT(count == tqp->op_count); + for (tmp = tqp->tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; + PR_ASSERT(count == tqp->op_count); } #endif /* defined(DEBUG) */ @@ -486,47 +503,48 @@ done: static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op) { pt_Continuation *next; + struct pt_TimedQueue *tqp = &pt_tqp[op->io_tq_index]; #if defined(DEBUG) { PRIntn count; pt_Continuation *tmp; - PR_ASSERT(pt_tq.head != NULL); - PR_ASSERT(pt_tq.tail != NULL); - PR_ASSERT(pt_tq.op_count != 0); - PR_ASSERT(pt_tq.head->prev == NULL); - PR_ASSERT(pt_tq.tail->next == NULL); - if (pt_tq.op_count > 1) + PR_ASSERT(tqp->head != NULL); + PR_ASSERT(tqp->tail != NULL); + PR_ASSERT(tqp->op_count != 0); + PR_ASSERT(tqp->head->prev == NULL); + PR_ASSERT(tqp->tail->next == NULL); + if (tqp->op_count > 1) { - PR_ASSERT(pt_tq.head->next != NULL); - PR_ASSERT(pt_tq.tail->prev != NULL); + PR_ASSERT(tqp->head->next != NULL); + PR_ASSERT(tqp->tail->prev != NULL); } else { - PR_ASSERT(pt_tq.head->next == NULL); - PR_ASSERT(pt_tq.tail->prev == NULL); + PR_ASSERT(tqp->head->next == NULL); + PR_ASSERT(tqp->tail->prev == NULL); } - for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; - PR_ASSERT(count == pt_tq.op_count); - for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; - PR_ASSERT(count == pt_tq.op_count); + for (tmp = tqp->head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; + PR_ASSERT(count == tqp->op_count); + for (tmp = tqp->tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; + PR_ASSERT(count == tqp->op_count); } #endif /* defined(DEBUG) */ /* remove this one from the list */ - if (NULL == op->prev) pt_tq.head = op->next; + if (NULL == op->prev) tqp->head = op->next; else op->prev->next = op->next; - if (NULL == op->next) pt_tq.tail = op->prev; + if (NULL == op->next) tqp->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; + if (op == tqp->op) tqp->op = op->prev; next = op->next; op->next = op->prev = NULL; op->status = pt_continuation_done; - pt_tq.op_count -= 1; + tqp->op_count -= 1; #if defined(DEBUG) pt_debug.continuationsServed += 1; @@ -537,12 +555,12 @@ static pt_Continuation *pt_FinishTimedInternal(pt_Continuation *op) { PRIntn count; pt_Continuation *tmp; - PR_ASSERT((pt_tq.head == NULL) == (pt_tq.tail == NULL)); - PR_ASSERT((pt_tq.head == NULL) == (pt_tq.op_count == 0)); - for (tmp = pt_tq.head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; - PR_ASSERT(count == pt_tq.op_count); - for (tmp = pt_tq.tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; - PR_ASSERT(count == pt_tq.op_count); + PR_ASSERT((tqp->head == NULL) == (tqp->tail == NULL)); + PR_ASSERT((tqp->head == NULL) == (tqp->op_count == 0)); + for (tmp = tqp->head, count = 0; tmp != NULL; tmp = tmp->next) count += 1; + PR_ASSERT(count == tqp->op_count); + for (tmp = tqp->tail, count = 0; tmp != NULL; tmp = tmp->prev) count += 1; + PR_ASSERT(count == tqp->op_count); } #endif /* defined(DEBUG) */ @@ -556,14 +574,16 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) PRThreadPriority priority; /* used to save caller's prio */ PRIntn pollingListUsed; /* # entries used in the list */ PRIntn pollingListNeeded; /* # entries needed this time */ - static struct pollfd *pollingList = 0; /* list built for polling */ - static PRIntn pollingSlotsAllocated = 0;/* # entries available in list */ - static pt_Continuation **pollingOps = 0;/* list paralleling polling list */ + PRIntn io_tq_index = my_op->io_tq_index; + struct pt_TimedQueue *tqp = &pt_tqp[my_op->io_tq_index]; + struct pollfd *pollingList = tqp->pollingList; + PRIntn pollingSlotsAllocated = tqp->pollingSlotsAllocated; + pt_Continuation **pollingOps = tqp->pollingOps; - PR_Unlock(pt_tq.ml); /* don't need that silly lock for a bit */ + PR_Unlock(tqp->ml); /* don't need that silly lock for a bit */ - priority = PR_GetThreadPriority(pt_tq.thread); - PR_SetThreadPriority(pt_tq.thread, PR_PRIORITY_HIGH); + priority = PR_GetThreadPriority(tqp->thread); + PR_SetThreadPriority(tqp->thread, PR_PRIORITY_HIGH); mx_poll_ticks = (PRInt32)PR_MillisecondsToInterval(PT_DEFAULT_POLL_MSEC); @@ -576,12 +596,12 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) PRIntervalTime now; pt_Continuation *op, *next_op; - PR_ASSERT(NULL != pt_tq.head); + PR_ASSERT(NULL != tqp->head); - pollingListNeeded = pt_tq.op_count; + pollingListNeeded = tqp->op_count; /* - * We are not holding the pt_tq.ml lock now, so more items may + * We are not holding the tqp->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. * @@ -598,9 +618,12 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) sizeof(pt_Continuation**) + pollingListNeeded * (sizeof(struct pollfd) + sizeof(pt_Continuation*))); PR_ASSERT(NULL != pollingOps); + tqp->pollingOps = pollingOps; pollingSlotsAllocated = pollingListNeeded; + tqp->pollingSlotsAllocated = pollingSlotsAllocated; pollingOps[pollingSlotsAllocated] = (pt_Continuation*)-1; pollingList = (struct pollfd*)(&pollingOps[pollingSlotsAllocated + 1]); + tqp->pollingList = pollingList; } @@ -619,10 +642,10 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) ** (perhaps) resetting it are safe 'cause it's the only modifiable ** bit in that word. */ - if (pt_tq.thread->state & PT_THREAD_ABORTED) + if (tqp->thread->state & PT_THREAD_ABORTED) { my_op->status = pt_continuation_abort; - pt_tq.thread->state &= ~PT_THREAD_ABORTED; + tqp->thread->state &= ~PT_THREAD_ABORTED; } @@ -633,9 +656,9 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) * There is an assertion that the operation is in progress. */ pollingListUsed = 0; - PR_Lock(pt_tq.ml); + PR_Lock(tqp->ml); - for (op = pt_tq.head; NULL != op;) + for (op = tqp->head; NULL != op;) { if (pt_continuation_abort == op->status) { @@ -644,7 +667,7 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) next_op = pt_FinishTimedInternal(op); if (op == my_op) goto recycle; else op = next_op; - PR_ASSERT(NULL != pt_tq.head); + PR_ASSERT(NULL != tqp->head); } else { @@ -657,7 +680,13 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) break; } PR_ASSERT((pt_Continuation*)-1 == pollingOps[pollingSlotsAllocated]); - op->fd->secret->eventMask = 0xffff; + /* + * eventMask bitmasks are declared as PRIntn so that + * each bitmask can be updated individually without + * disturbing adjacent memory, but only the lower 16 + * bits of a bitmask are used. + */ + op->fd->secret->eventMask[io_tq_index] = 0xffff; pollingOps[pollingListUsed] = op; pollingList[pollingListUsed].revents = 0; pollingList[pollingListUsed].fd = op->arg1.osfd; @@ -674,17 +703,17 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) * 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) + if (PR_INTERVAL_NO_TIMEOUT == tqp->head->timeout) msecs = PT_DEFAULT_POLL_MSEC; else { - timeout = pt_tq.head->absolute - PR_IntervalNow(); + timeout = tqp->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); } - PR_Unlock(pt_tq.ml); + PR_Unlock(tqp->ml); /* * If 'op' isn't NULL at this point, then we didn't get to @@ -731,15 +760,15 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) if ((revents & POLLNVAL) /* busted in all cases */ || ((events & POLLOUT) && (revents & POLLHUP))) /* write op & hup */ { - PR_Lock(pt_tq.ml); + PR_Lock(tqp->ml); op->result.code = -1; if (POLLNVAL & revents) op->syserrno = EBADF; else if (POLLHUP & revents) op->syserrno = EPIPE; (void)pt_FinishTimedInternal(op); if (op == my_op) goto recycle; - PR_Unlock(pt_tq.ml); + PR_Unlock(tqp->ml); } - else if ((0 != (revents & op->fd->secret->eventMask)) + else if ((0 != (revents & op->fd->secret->eventMask[io_tq_index])) && (pt_continuation_pending == op->status)) { /* @@ -751,10 +780,10 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) if (op->function(op, revents)) { - PR_Lock(pt_tq.ml); + PR_Lock(tqp->ml); (void)pt_FinishTimedInternal(op); if (op == my_op) goto recycle; - PR_Unlock(pt_tq.ml); + PR_Unlock(tqp->ml); } else { @@ -773,7 +802,7 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) * we won't invoke the continuation * function again. */ - op->fd->secret->eventMask &= ~revents; + op->fd->secret->eventMask[io_tq_index] &= ~revents; } } } @@ -785,11 +814,11 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) * wire are lucky, but none the less, valid. */ now = PR_IntervalNow(); - PR_Lock(pt_tq.ml); - while ((NULL != pt_tq.head) - && (PR_INTERVAL_NO_TIMEOUT != pt_tq.head->timeout)) + PR_Lock(tqp->ml); + while ((NULL != tqp->head) + && (PR_INTERVAL_NO_TIMEOUT != tqp->head->timeout)) { - op = pt_tq.head; /* get a copy of this before finishing it */ + op = tqp->head; /* get a copy of this before finishing it */ if ((PRInt32)(op->absolute - now) > 0) break; /* * The head element of the timed queue has timed out. Record @@ -806,7 +835,7 @@ static void pt_ContinuationThreadInternal(pt_Continuation *my_op) */ if (op == my_op) goto recycle; /* exit w/o unlocking */ } - PR_Unlock(pt_tq.ml); + PR_Unlock(tqp->ml); } PR_NOT_REACHED("This is a while(true) loop /w no breaks"); @@ -822,7 +851,7 @@ recycle: ** to 'recycle' and notifying the condition. ** ** Selecting a likely thread seems like magic. I'm going to try - ** using one that has the longest (or no) timeout, pt_tq.tail. + ** using one that has the longest (or no) timeout, tqp->tail. ** If that slot's empty, then there's no outstanding I/O and we ** don't need a thread at all. ** @@ -831,19 +860,19 @@ recycle: */ /* $$$ should this be called with the lock held? $$$ */ - PR_SetThreadPriority(pt_tq.thread, priority); /* reset back to caller's */ + PR_SetThreadPriority(tqp->thread, priority); /* reset back to caller's */ - PR_ASSERT((NULL == pt_tq.head) == (0 == pt_tq.op_count)); - PR_ASSERT((NULL == pt_tq.head) == (NULL == pt_tq.tail)); + PR_ASSERT((NULL == tqp->head) == (0 == tqp->op_count)); + PR_ASSERT((NULL == tqp->head) == (NULL == tqp->tail)); PR_ASSERT(pt_continuation_done == my_op->status); - if (NULL != pt_tq.tail) + if (NULL != tqp->tail) { - if (pt_tq.tail->status != pt_continuation_abort) + if (tqp->tail->status != pt_continuation_abort) { - pt_tq.tail->status = pt_continuation_recycle; + tqp->tail->status = pt_continuation_recycle; } - PR_NotifyCondVar(pt_tq.tail->complete); + PR_NotifyCondVar(tqp->tail->complete); #if defined(DEBUG) pt_debug.recyclesNeeded += 1; #endif @@ -858,13 +887,25 @@ static PRIntn pt_Continue(pt_Continuation *op) { PRStatus rv; PRThread *self = PR_GetCurrentThread(); + struct pt_TimedQueue *tqp; + + /* lazy assignment of the thread's ioq */ + if (-1 == self->io_tq_index) + { + self->io_tq_index = (PR_AtomicIncrement(&_pt_tq_index)-1) % _pt_tq_count; + } + + PR_ASSERT(self->io_tq_index >= 0); + tqp = &pt_tqp[self->io_tq_index]; + /* lazy allocation of the thread's cv */ if (NULL == self->io_cv) - self->io_cv = PR_NewCondVar(pt_tq.ml); + self->io_cv = PR_NewCondVar(tqp->ml); /* Finish filling in the blank slots */ op->complete = self->io_cv; op->status = pt_continuation_pending; /* set default value */ - PR_Lock(pt_tq.ml); /* we provide the locking */ + op->io_tq_index = self->io_tq_index; + PR_Lock(tqp->ml); /* we provide the locking */ pt_InsertTimedInternal(op); /* insert in the structure */ @@ -874,7 +915,7 @@ static PRIntn pt_Continue(pt_Continuation *op) */ do { - if (NULL == pt_tq.thread) + if (NULL == tqp->thread) { /* ** We're the one. Call the processing function with the lock @@ -882,10 +923,10 @@ static PRIntn pt_Continue(pt_Continuation *op) ** will certainly be times within the function when it gets ** released. */ - pt_tq.thread = self; /* I'm taking control */ + tqp->thread = self; /* I'm taking control */ pt_ContinuationThreadInternal(op); /* go slash and burn */ PR_ASSERT(pt_continuation_done == op->status); - pt_tq.thread = NULL; /* I'm abdicating my rule */ + tqp->thread = NULL; /* I'm abdicating my rule */ } else { @@ -938,7 +979,7 @@ static PRIntn pt_Continue(pt_Continuation *op) } while (pt_continuation_done != op->status); - PR_Unlock(pt_tq.ml); /* we provided the locking */ + PR_Unlock(tqp->ml); /* we provided the locking */ return op->result.code; /* and the primary answer */ } /* pt_Continue */ @@ -1151,16 +1192,33 @@ static PRBool pt_recvfrom_cont(pt_Continuation *op, PRInt16 revents) } /* pt_recvfrom_cont */ #ifdef AIX -static PRBool pt_aix_transmitfile_cont(pt_Continuation *op, PRInt16 revents) +static PRBool pt_aix_sendfile_cont(pt_Continuation *op, PRInt16 revents) { struct sf_parms *sf_struct = (struct sf_parms *) op->arg2.buffer; int rv; + long long saved_file_offset; + long long saved_file_bytes; + saved_file_offset = sf_struct->file_offset; + saved_file_bytes = sf_struct->file_bytes; + sf_struct->bytes_sent = 0; + + if ((sf_struct->file_bytes > 0) && (sf_struct->file_size > 0)) + PR_ASSERT((sf_struct->file_bytes + sf_struct->file_offset) <= + sf_struct->file_size); rv = AIX_SEND_FILE(&op->arg1.osfd, sf_struct, op->arg4.flags); op->syserrno = errno; if (rv != -1) { op->result.code += sf_struct->bytes_sent; + /* + * A bug in AIX 4.3.2 prevents the 'file_bytes' field from + * being updated. So, 'file_bytes' is maintained by NSPR to + * avoid conflict when this bug is fixed in AIX, in the future. + */ + if (saved_file_bytes != -1) + saved_file_bytes -= (sf_struct->file_offset - saved_file_offset); + sf_struct->file_bytes = saved_file_bytes; } else if (op->syserrno != EWOULDBLOCK && op->syserrno != EAGAIN) { op->result.code = -1; } else { @@ -1176,13 +1234,13 @@ static PRBool pt_aix_transmitfile_cont(pt_Continuation *op, PRInt16 revents) #endif /* AIX */ #ifdef HPUX11 -static PRBool pt_hpux_transmitfile_cont(pt_Continuation *op, PRInt16 revents) +static PRBool pt_hpux_sendfile_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); + count = sendfile(op->arg1.osfd, op->filedesc, op->arg3.file_spec.offset, + op->arg3.file_spec.nbytes, hdtrl, op->arg4.flags); PR_ASSERT(count <= op->nbytes_to_send); op->syserrno = errno; @@ -1193,20 +1251,41 @@ static PRBool pt_hpux_transmitfile_cont(pt_Continuation *op, PRInt16 revents) } 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; + if (count < hdtrl[0].iov_len) { + /* header not sent */ + + hdtrl[0].iov_base = ((char *) hdtrl[0].iov_len) + count; hdtrl[0].iov_len -= count; - } else { - op->arg3.offset = count - hdtrl[0].iov_len; + + } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes)) { + /* header sent, file not sent */ + PRUint32 file_nbytes_sent = count - hdtrl[0].iov_len; + hdtrl[0].iov_base = NULL; hdtrl[0].iov_len = 0; - } + + op->arg3.file_spec.offset += file_nbytes_sent; + op->arg3.file_spec.nbytes -= file_nbytes_sent; + } else if (count < (hdtrl[0].iov_len + op->arg3.file_spec.nbytes + + hdtrl[1].iov_len)) { + PRUint32 trailer_nbytes_sent = count - (hdtrl[0].iov_len + + op->arg3.file_spec.nbytes); + + /* header sent, file sent, trailer not sent */ + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + /* + * set file offset and len so that no more file data is + * sent + */ + op->arg3.file_spec.offset = op->arg3.file_spec.st_size; + op->arg3.file_spec.nbytes = 0; + + hdtrl[1].iov_base =((char *) hdtrl[1].iov_base)+ trailer_nbytes_sent; + hdtrl[1].iov_len -= trailer_nbytes_sent; + } op->nbytes_to_send -= count; return PR_FALSE; } @@ -1215,18 +1294,57 @@ static PRBool pt_hpux_transmitfile_cont(pt_Continuation *op, PRInt16 revents) } #endif /* HPUX11 */ +#define _MD_CPUS_ONLINE 2 + void _PR_InitIO() { - pt_tq.ml = PR_NewLock(); - PR_ASSERT(NULL != pt_tq.ml); + PRIntn index; + char *num_io_queues; + + if (num_io_queues = getenv("NSPR_NUM_IO_QUEUES")) + { + _pt_tq_count = atoi(num_io_queues); + } + else + { + /* + * Get the number of CPUs if the pthread + * library has kernel-scheduled entities that + * can run on multiple CPUs. + */ +#ifdef HPUX11 + _pt_num_cpus = pthread_num_processors_np(); +#elif defined(IRIX) || defined(OSF1) + _pt_num_cpus = sysconf(_SC_NPROC_ONLN); +#elif defined(AIX) || defined(LINUX) || defined(SOLARIS) + _pt_num_cpus = sysconf(_SC_NPROCESSORS_ONLN); +#else + /* + * A pure user-level (Mx1) pthread library can + * only use one CPU, even on a multiprocessor. + */ + _pt_num_cpus = 1; +#endif + if (_pt_num_cpus < 0) + _pt_num_cpus = _MD_CPUS_ONLINE; + _pt_tq_count = _pt_num_cpus; + } + + pt_tqp = (struct pt_TimedQueue *) + PR_CALLOC(_pt_tq_count * sizeof(struct pt_TimedQueue)); + PR_ASSERT(NULL != pt_tqp); + + for (index = 0; index < _pt_tq_count; index++) + { + pt_tqp[index].ml = PR_NewLock(); + PR_ASSERT(NULL != pt_tqp[index].ml); + } #if defined(DEBUG) memset(&pt_debug, 0, sizeof(PTDebug)); pt_debug.timeStarted = PR_Now(); #endif - pt_tq.thread = NULL; - _pr_flock_lock = PR_NewLock(); PR_ASSERT(NULL != _pr_flock_lock); _pr_rename_lock = PR_NewLock(); @@ -1384,79 +1502,80 @@ static PRInt32 pt_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) static PRInt32 pt_Writev( PRFileDesc *fd, const PRIOVec *iov, PRInt32 iov_len, PRIntervalTime timeout) { - PRIntn iov_index = 0; + PRIntn iov_index; PRBool fNeedContinue = PR_FALSE; - PRInt32 syserrno, bytes = -1, rv = -1; + PRInt32 syserrno, bytes, rv = -1; + struct iovec osiov_local[PR_MAX_IOVECTOR_SIZE], *osiov; + int osiov_len; 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, (const struct iovec*)iov, iov_len); - syserrno = errno; + /* Ensured by PR_Writev */ + PR_ASSERT(iov_len <= PR_MAX_IOVECTOR_SIZE); /* - * 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! + * We can't pass iov to writev because PRIOVec and struct iovec + * may not be binary compatible. Make osiov a copy of iov and + * pass osiov to writev. We can modify osiov if we need to + * continue the operation. */ - if ((bytes > 0) && (!fd->secret->nonblocking)) + osiov = osiov_local; + osiov_len = iov_len; + for (iov_index = 0; iov_index < osiov_len; iov_index++) { - 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 */ - } + osiov[iov_index].iov_base = iov[iov_index].iov_base; + osiov[iov_index].iov_len = iov[iov_index].iov_len; } - if ((bytes >= 0) && (iov_index < iov_len) && (!fd->secret->nonblocking)) + rv = bytes = writev(fd->secret->md.osfd, osiov, osiov_len); + syserrno = errno; + + if (!fd->secret->nonblocking) { - if (PR_INTERVAL_NO_WAIT == timeout) + if (bytes >= 0) { - rv = -1; - syserrno = ETIMEDOUT; + /* + * If we moved some bytes, 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! + */ + for ( ;osiov_len > 0; osiov++, osiov_len--) + { + if (bytes < osiov->iov_len) + { + /* this one's not done yet */ + osiov->iov_base = (char*)osiov->iov_base + bytes; + osiov->iov_len -= bytes; + break; /* go off and do that */ + } + bytes -= osiov->iov_len; /* this one's done cooked */ + } + PR_ASSERT(osiov_len > 0 || bytes == 0); + if (osiov_len > 0) + { + if (PR_INTERVAL_NO_WAIT == timeout) + { + rv = -1; + syserrno = ETIMEDOUT; + } + else fNeedContinue = PR_TRUE; + } } - else fNeedContinue = PR_TRUE; - } - else if ((bytes == -1) && (syserrno == EWOULDBLOCK || syserrno == EAGAIN) - && (!fd->secret->nonblocking)) - { - if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; - else + else if (syserrno == EWOULDBLOCK || syserrno == EAGAIN) { - rv = bytes = 0; - fNeedContinue = PR_TRUE; + if (PR_INTERVAL_NO_WAIT == timeout) syserrno = ETIMEDOUT; + else + { + rv = 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.fd = fd; op.arg1.osfd = fd->secret->md.osfd; @@ -1468,7 +1587,6 @@ static PRInt32 pt_Writev( 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; @@ -2037,11 +2155,10 @@ static void pt_aix_sendfile_init_routine(void) } /* - * pt_AIXDispatchTransmitFile + * pt_AIXDispatchSendFile */ -static PRInt32 pt_AIXDispatchTransmitFile(PRFileDesc *sd, PRFileDesc *fd, - const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, - PRIntervalTime timeout) +static PRInt32 pt_AIXDispatchSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) { int rv; @@ -2049,18 +2166,19 @@ static PRInt32 pt_AIXDispatchTransmitFile(PRFileDesc *sd, PRFileDesc *fd, pt_aix_sendfile_init_routine); PR_ASSERT(0 == rv); if (pt_aix_sendfile_fptr) { - return pt_AIXTransmitFile(sd, fd, headers, hlen, flags, timeout); + return pt_AIXSendFile(sd, sfd, flags, timeout); } else { - return _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout); + return _PR_UnixSendFile(sd, sfd, flags, timeout); } } #endif /* !HAVE_SEND_FILE */ + /* - * pt_AIXTransmitFile + * pt_AIXSendFile * - * Send file fd across socket sd. If headers is non-NULL, 'hlen' - * bytes of headers is sent before sending the file. + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. * * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file * @@ -2070,27 +2188,34 @@ static PRInt32 pt_AIXDispatchTransmitFile(PRFileDesc *sd, PRFileDesc *fd, * call available in AIX 4.3.2. */ -static PRInt32 pt_AIXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, - const void *headers, PRInt32 hlen, PRTransmitFileFlags flags, - PRIntervalTime timeout) +static PRInt32 pt_AIXSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) { struct sf_parms sf_struct; uint_t send_flags; ssize_t rv; int syserrno; PRInt32 count; + long long saved_file_offset; + long long saved_file_bytes; - sf_struct.header_data = (void *) headers; /* cast away the 'const' */ - sf_struct.header_length = hlen; - sf_struct.file_descriptor = fd->secret->md.osfd; + sf_struct.header_data = (void *) sfd->header; /* cast away the 'const' */ + sf_struct.header_length = sfd->hlen; + sf_struct.file_descriptor = sfd->fd->secret->md.osfd; sf_struct.file_size = 0; - sf_struct.file_offset = 0; - sf_struct.file_bytes = -1; - sf_struct.trailer_data = NULL; - sf_struct.trailer_length = 0; + sf_struct.file_offset = sfd->file_offset; + if (sfd->file_nbytes == 0) + sf_struct.file_bytes = -1; + else + sf_struct.file_bytes = sfd->file_nbytes; + sf_struct.trailer_data = (void *) sfd->trailer; + sf_struct.trailer_length = sfd->tlen; sf_struct.bytes_sent = 0; - send_flags = 0; + saved_file_offset = sf_struct.file_offset; + saved_file_bytes = sf_struct.file_bytes; + + send_flags = 0; /* flags processed at the end */ do { rv = AIX_SEND_FILE(&sd->secret->md.osfd, &sf_struct, send_flags); @@ -2104,6 +2229,14 @@ static PRInt32 pt_AIXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, } } else { count = sf_struct.bytes_sent; + /* + * A bug in AIX 4.3.2 prevents the 'file_bytes' field from + * being updated. So, 'file_bytes' is maintained by NSPR to + * avoid conflict when this bug is fixed in AIX, in the future. + */ + if (saved_file_bytes != -1) + saved_file_bytes -= (sf_struct.file_offset - saved_file_offset); + sf_struct.file_bytes = saved_file_bytes; } if ((rv == 1) || ((rv == -1) && (count == 0))) { @@ -2115,7 +2248,7 @@ static PRInt32 pt_AIXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, op.arg4.flags = send_flags; op.result.code = count; op.timeout = timeout; - op.function = pt_aix_transmitfile_cont; + op.function = pt_aix_sendfile_cont; op.event = POLLOUT | POLLPRI; count = pt_Continue(&op); syserrno = op.syserrno; @@ -2128,16 +2261,20 @@ static PRInt32 pt_AIXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { PR_Close(sd); } + PR_ASSERT(count == (sfd->hlen + sfd->tlen + + ((sfd->file_nbytes == 0) ? + sf_struct.file_size - sfd->file_offset : + sfd->file_nbytes))); return count; } #endif /* AIX */ #ifdef HPUX11 /* - * pt_HPUXTransmitFile + * pt_HPUXSendFile * - * Send file fd across socket sd. If headers is non-NULL, 'hlen' - * bytes of headers is sent before sending the file. + * Send file sfd->fd across socket sd. If specified, header and trailer + * buffers are sent before and after the file, respectively. * * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file * @@ -2147,28 +2284,30 @@ static PRInt32 pt_AIXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, * 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) +static PRInt32 pt_HPUXSendFile(PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) { struct stat statbuf; - size_t nbytes_to_send; + size_t nbytes_to_send, file_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) { + if (fstat(sfd->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; + file_nbytes_to_send = (sfd->file_nbytes == 0) ? + statbuf.st_size - sfd->file_offset : + sfd->file_nbytes; + nbytes_to_send = sfd->hlen + sfd->tlen + file_nbytes_to_send; + + hdtrl[0].iov_base = (void *) sfd->header; /* cast away the 'const' */ + hdtrl[0].iov_len = sfd->hlen; + hdtrl[1].iov_base = (void *) sfd->trailer; + hdtrl[1].iov_len = sfd->tlen; /* * SF_DISCONNECT seems to close the socket even if sendfile() * only does a partial send on a nonblocking socket. This @@ -2178,9 +2317,8 @@ static PRInt32 pt_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, 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); + count = sendfile(sd->secret->md.osfd, sfd->fd->secret->md.osfd, + sfd->file_offset, file_nbytes_to_send, hdtrl, send_flags); } while (count == -1 && (syserrno = errno) == EINTR); if (count == -1 && (syserrno == EAGAIN || syserrno == EWOULDBLOCK)) { @@ -2189,25 +2327,50 @@ static PRInt32 pt_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, 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 { + if (count < sfd->hlen) { + /* header not sent */ + + hdtrl[0].iov_base = ((char *) sfd->header) + count; + hdtrl[0].iov_len = sfd->hlen - count; + op.arg3.file_spec.offset = sfd->file_offset; + op.arg3.file_spec.nbytes = file_nbytes_to_send; + } else if (count < (sfd->hlen + file_nbytes_to_send)) { + /* header sent, file not sent */ + hdtrl[0].iov_base = NULL; hdtrl[0].iov_len = 0; - op.arg3.offset = count - hlen; - } + + op.arg3.file_spec.offset = sfd->file_offset + count - sfd->hlen; + op.arg3.file_spec.nbytes = file_nbytes_to_send - (count - sfd->hlen); + } else if (count < (sfd->hlen + file_nbytes_to_send + sfd->tlen)) { + PRUint32 trailer_nbytes_sent; + + /* header sent, file sent, trailer not sent */ + + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + /* + * set file offset and len so that no more file data is + * sent + */ + op.arg3.file_spec.offset = statbuf.st_size; + op.arg3.file_spec.nbytes = 0; + + trailer_nbytes_sent = count - sfd->hlen - file_nbytes_to_send; + hdtrl[1].iov_base = ((char *) sfd->trailer) + trailer_nbytes_sent; + hdtrl[1].iov_len = sfd->tlen - trailer_nbytes_sent; + } op.fd = sd; op.arg1.osfd = sd->secret->md.osfd; - op.filedesc = fd->secret->md.osfd; + op.filedesc = sfd->fd->secret->md.osfd; op.arg2.buffer = hdtrl; + op.arg3.file_spec.st_size = statbuf.st_size; 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.function = pt_hpux_sendfile_cont; op.event = POLLOUT | POLLPRI; count = pt_Continue(&op); syserrno = op.syserrno; @@ -2220,33 +2383,27 @@ static PRInt32 pt_HPUXTransmitFile(PRFileDesc *sd, PRFileDesc *fd, if (flags & PR_TRANSMITFILE_CLOSE_SOCKET) { PR_Close(sd); } + PR_ASSERT(count == nbytes_to_send); 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; - /* The socket must be in blocking mode. */ - if (sd->secret->nonblocking) - { - PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); - return -1; - } + PRSendFileData sfd; -#ifdef HPUX11 - return pt_HPUXTransmitFile(sd, fd, headers, hlen, flags, timeout); -#elif defined(AIX) -#ifdef HAVE_SEND_FILE - return pt_AIXTransmitFile(sd, fd, headers, hlen, flags, timeout); -#else - return pt_AIXDispatchTransmitFile(sd, fd, headers, hlen, flags, timeout); -#endif /* HAVE_SEND_FILE */ -#else - return _PR_UnixTransmitFile(sd, fd, headers, hlen, flags, timeout); -#endif + sfd.fd = fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = headers; + sfd.hlen = hlen; + sfd.trailer = NULL; + sfd.tlen = 0; + + return(PR_SendFile(sd, &sfd, flags, timeout)); } /* pt_TransmitFile */ /* @@ -2831,7 +2988,15 @@ static PRFileDesc *pt_SetMethods(PRIntn osfd, PRDescType type) fd->secret->md.osfd = osfd; fd->secret->state = _PR_FILEDESC_OPEN; /* By default, a Unix fd is not closed on exec. */ - PR_ASSERT(0 == fcntl(osfd, F_GETFD, 0)); +#ifdef DEBUG + /* + * Ignore EBADF error on fd's 0, 1, 2 because they are + * not open in all processes. + */ + flags = fcntl(osfd, F_GETFD, 0); + PR_ASSERT((0 == flags) || (-1 == flags + && (0 <= osfd && osfd <= 2) && errno == EBADF)); +#endif fd->secret->inheritable = PR_TRUE; switch (type) { @@ -3421,55 +3586,34 @@ PR_IMPLEMENT(PRDirEntry*) PR_ReadDir(PRDir *dir, PRDirFlags flags) 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; + return PR_Socket(domain, SOCK_DGRAM, 0); } /* 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); + return PR_Socket(domain, SOCK_STREAM, 0); +} /* PR_NewTCPSocket */ - 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_IMPLEMENT(PRFileDesc*) PR_OpenUDPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_DGRAM, 0); +} /* PR_NewUDPSocket */ + +PR_IMPLEMENT(PRFileDesc*) PR_OpenTCPSocket(PRIntn af) +{ + return PR_Socket(af, SOCK_STREAM, 0); } /* PR_NewTCPSocket */ PR_IMPLEMENT(PRStatus) PR_NewTCPSocketPair(PRFileDesc *fds[2]) @@ -3970,6 +4114,44 @@ retry: return rv; } +#ifdef AIX +extern int _pr_aix_send_file_use_disabled; +#endif + +PR_IMPLEMENT(PRInt32) PR_SendFile( + PRFileDesc *sd, PRSendFileData *sfd, + PRTransmitFileFlags flags, PRIntervalTime timeout) +{ + if (pt_TestAbort()) return -1; + /* The socket must be in blocking mode. */ + if (sd->secret->nonblocking) + { + PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); + return -1; + } +#ifdef HPUX11 + return(pt_HPUXSendFile(sd, sfd, flags, timeout)); +#elif defined(AIX) +#ifdef HAVE_SEND_FILE + /* + * A bug in AIX 4.3.2 results in corruption of data transferred by + * send_file(); AIX patch PTF U463956 contains the fix. A user can + * disable the use of send_file function in NSPR, when this patch is + * not installed on the system, by setting the envionment variable + * NSPR_AIX_SEND_FILE_USE_DISABLED to 1. + */ + if (_pr_aix_send_file_use_disabled) + return(_PR_UnixSendFile(sd, sfd, flags, timeout)); + else + return(pt_AIXSendFile(sd, sfd, flags, timeout)); +#else + return(_PR_UnixSendFile(sd, sfd, flags, timeout)); + /* return(pt_AIXDispatchSendFile(sd, sfd, flags, timeout));*/ +#endif /* HAVE_SEND_FILE */ +#else + return(_PR_UnixSendFile(sd, sfd, flags, timeout)); +#endif +} #endif /* defined(_PR_PTHREADS) */ /* ptio.c */ diff --git a/pr/src/pthreads/ptmisc.c b/pr/src/pthreads/ptmisc.c index 1cd4a8f2..ce6d663f 100644 --- a/pr/src/pthreads/ptmisc.c +++ b/pr/src/pthreads/ptmisc.c @@ -26,6 +26,9 @@ #include "primpl.h" #include <stdio.h> +#ifdef SOLARIS +#include <thread.h> +#endif #define PT_LOG(f) @@ -33,7 +36,13 @@ void _PR_InitCPUs(void) {PT_LOG("_PR_InitCPUs")} void _PR_InitStacks(void) {PT_LOG("_PR_InitStacks")} PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) - {PT_LOG("PR_SetConcurrency")} +{ +#ifdef SOLARIS + thr_setconcurrency(numCPUs); +#else + PT_LOG("PR_SetConcurrency"); +#endif +} PR_IMPLEMENT(void) PR_SetThreadRecycleMode(PRUint32 flag) {PT_LOG("PR_SetThreadRecycleMode")} diff --git a/pr/src/pthreads/ptsynch.c b/pr/src/pthreads/ptsynch.c index 2d61ee8c..7beaf450 100644 --- a/pr/src/pthreads/ptsynch.c +++ b/pr/src/pthreads/ptsynch.c @@ -649,6 +649,319 @@ PR_IMPLEMENT(PRSemaphore*) PR_NewSem(PRUintn value) return NULL; } +#ifdef _PR_HAVE_POSIX_SEMAPHORES +#include <fcntl.h> + +PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( + const char *name, + PRIntn flags, + PRIntn mode, + PRUintn value) +{ + PRSem *sem; + char osname[PR_IPC_NAME_SIZE]; + + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) + { + return NULL; + } + + sem = PR_NEW(PRSem); + if (NULL == sem) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + if (flags & PR_SEM_CREATE) + { + int oflag = O_CREAT; + + if (flags & PR_SEM_EXCL) oflag |= O_EXCL; + sem->sem = sem_open(osname, oflag, mode, value); + } + else + { + sem->sem = sem_open(osname, 0); + } + if ((sem_t *) -1 == sem->sem) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_DELETE(sem); + return NULL; + } + return sem; +} + +PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) +{ + int rv; + rv = sem_wait(sem->sem); + if (0 != rv) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) +{ + int rv; + rv = sem_post(sem->sem); + if (0 != rv) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) +{ + int rv; + rv = sem_close(sem->sem); + if (0 != rv) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + PR_DELETE(sem); + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) +{ + int rv; + char osname[PR_IPC_NAME_SIZE]; + + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) + { + return PR_FAILURE; + } + rv = sem_unlink(osname); + if (0 != rv) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +#endif /* _PR_HAVE_POSIX_SEMAPHORES */ + +#ifdef _PR_HAVE_SYSV_SEMAPHORES + +#include <fcntl.h> +#include <sys/sem.h> + +/* + * From the semctl(2) man page in glibc 2.0 + */ +#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) +/* union semun is defined by including <sys/sem.h> */ +#else +/* according to X/OPEN we have to define it ourselves */ +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; +}; +#endif + +/* + * 'a' (97) is the final closing price of NSCP stock. + */ +#define NSPR_IPC_KEY_ID 'a' /* the id argument for ftok() */ + +#define NSPR_SEM_MODE 0666 + +PR_IMPLEMENT(PRSem *) PR_OpenSemaphore( + const char *name, + PRIntn flags, + PRIntn mode, + PRUintn value) +{ + PRSem *sem; + key_t key; + union semun arg; + struct sembuf sop; + struct semid_ds seminfo; +#define MAX_TRIES 60 + PRIntn i; + char osname[PR_IPC_NAME_SIZE]; + + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) + { + return NULL; + } + + /* Make sure the file exists before calling ftok. */ + if (flags & PR_SEM_CREATE) + { + int osfd = open(osname, O_RDWR|O_CREAT, mode); + if (-1 == osfd) + { + _PR_MD_MAP_OPEN_ERROR(errno); + return NULL; + } + if (close(osfd) == -1) + { + _PR_MD_MAP_CLOSE_ERROR(errno); + return NULL; + } + } + key = ftok(osname, NSPR_IPC_KEY_ID); + if ((key_t)-1 == key) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return NULL; + } + + sem = PR_NEW(PRSem); + if (NULL == sem) + { + PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); + return NULL; + } + + if (flags & PR_SEM_CREATE) + { + sem->semid = semget(key, 1, mode|IPC_CREAT|IPC_EXCL); + if (sem->semid >= 0) + { + /* creator of a semaphore is responsible for initializing it */ + arg.val = 0; + if (semctl(sem->semid, 0, SETVAL, arg) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_DELETE(sem); + return NULL; + } + /* call semop to set sem_otime to nonzero */ + sop.sem_num = 0; + sop.sem_op = value; + sop.sem_flg = 0; + if (semop(sem->semid, &sop, 1) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_DELETE(sem); + return NULL; + } + return sem; + } + + if (errno != EEXIST || flags & PR_SEM_EXCL) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_DELETE(sem); + return NULL; + } + } + + sem->semid = semget(key, 1, NSPR_SEM_MODE); + if (sem->semid == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + PR_DELETE(sem); + return NULL; + } + for (i = 0; i < MAX_TRIES; i++) + { + arg.buf = &seminfo; + semctl(sem->semid, 0, IPC_STAT, arg); + if (seminfo.sem_otime != 0) break; + sleep(1); + } + if (i == MAX_TRIES) + { + PR_SetError(PR_IO_TIMEOUT_ERROR, 0); + PR_DELETE(sem); + return NULL; + } + return sem; +} + +PR_IMPLEMENT(PRStatus) PR_WaitSemaphore(PRSem *sem) +{ + struct sembuf sop; + + sop.sem_num = 0; + sop.sem_op = -1; + sop.sem_flg = 0; + if (semop(sem->semid, &sop, 1) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_PostSemaphore(PRSem *sem) +{ + struct sembuf sop; + + sop.sem_num = 0; + sop.sem_op = 1; + sop.sem_flg = 0; + if (semop(sem->semid, &sop, 1) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_CloseSemaphore(PRSem *sem) +{ + PR_DELETE(sem); + return PR_SUCCESS; +} + +PR_IMPLEMENT(PRStatus) PR_DeleteSemaphore(const char *name) +{ + key_t key; + int semid; + /* On some systems (e.g., glibc 2.0) semctl requires a fourth argument */ + union semun unused; + char osname[PR_IPC_NAME_SIZE]; + + if (_PR_MakeNativeIPCName(name, osname, sizeof(osname), _PRIPCSem) + == PR_FAILURE) + { + return PR_FAILURE; + } + key = ftok(osname, NSPR_IPC_KEY_ID); + if ((key_t) -1 == key) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + if (unlink(osname) == -1) + { + _PR_MD_MAP_UNLINK_ERROR(errno); + return PR_FAILURE; + } + semid = semget(key, 1, NSPR_SEM_MODE); + if (-1 == semid) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + unused.val = 0; + if (semctl(semid, 0, IPC_RMID, unused) == -1) + { + _PR_MD_MAP_DEFAULT_ERROR(errno); + return PR_FAILURE; + } + return PR_SUCCESS; +} + +#endif /* _PR_HAVE_SYSV_SEMAPHORES */ + /**************************************************************/ /**************************************************************/ /******************ROUTINES FOR DCE EMULATION******************/ diff --git a/pr/src/pthreads/ptthread.c b/pr/src/pthreads/ptthread.c index 943ea926..62e3452d 100644 --- a/pr/src/pthreads/ptthread.c +++ b/pr/src/pthreads/ptthread.c @@ -225,6 +225,7 @@ static PRThread* pt_AttachThread(void) PR_ASSERT(0 == rv); thred->state = PT_THREAD_GLOBAL | PT_THREAD_FOREIGN; + thred->io_tq_index = -1; PR_Lock(pt_book.ml); /* then put it into the list */ @@ -363,6 +364,8 @@ static PRThread* _PR_CreateThread( thred->stack->stackSize = stackSize; thred->stack->thr = thred; + thred->io_tq_index = -1; + #ifdef PT_NO_SIGTIMEDWAIT pthread_mutex_init(&thred->suspendResumeMutex,NULL); pthread_cond_init(&thred->suspendResumeCV,NULL); @@ -839,6 +842,8 @@ void _PR_InitThreads( thred->stack->thr = thred; _PR_InitializeStack(thred->stack); + thred->io_tq_index = -1; + /* * Create a key for our use to store a backpointer in the pthread * to our PRThread object. This object gets deleted when the thread diff --git a/pr/src/threads/combined/prucpu.c b/pr/src/threads/combined/prucpu.c index d7d9e84d..e4abf5e0 100644 --- a/pr/src/threads/combined/prucpu.c +++ b/pr/src/threads/combined/prucpu.c @@ -42,6 +42,7 @@ PRInt32 _pr_cpu_affinity_mask = 0; static PRUintn _pr_cpuID; static void PR_CALLBACK _PR_CPU_Idle(void *); + static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue); void _PR_InitCPUs() @@ -57,7 +58,8 @@ void _PR_InitCPUs() #endif #ifdef HAVE_CUSTOM_USER_THREADS - _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); + if (!_native_threads_only) + _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); #endif /* Now start the first CPU. */ @@ -111,7 +113,6 @@ static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue) */ cpu = PR_NEWZAP(_PRCPU); if (cpu) { - cpu->last_clock = PR_IntervalNow(); if (needQueue == PR_TRUE) @@ -131,23 +132,26 @@ static _PRCPU *_PR_CreateCPU(PRThread *thread, PRBool needQueue) _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->cpu = cpu; - - cpu->idle_thread->no_sched = 0; + if (!_native_threads_only) { + + 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->cpu = cpu; + + cpu->idle_thread->no_sched = 0; + } cpu->thread = thread; @@ -178,6 +182,22 @@ static void _PR_RunCPU(void *unused) PR_ASSERT(NULL != me); + /* + * _PR_CreateCPU calls _PR_CreateThread to create the + * idle thread. Because _PR_CreateThread calls PR_Lock, + * the current thread has to remain a global thread + * during the _PR_CreateCPU call so that it can wait for + * the lock if the lock is held by another thread. If + * we clear the _PR_GLOBAL_SCOPE flag in + * _PR_MD_CREATE_PRIMORDIAL_THREAD, the current thread + * will be treated as a local thread and have trouble + * waiting for the lock because the CPU is not fully + * constructed yet. + * + * After the CPU is created, it is safe to mark the + * current thread as a local thread. + */ + #ifdef HAVE_CUSTOM_USER_THREADS _PR_MD_CREATE_PRIMORDIAL_USER_THREAD(me); #endif @@ -185,6 +205,10 @@ static void _PR_RunCPU(void *unused) me->no_sched = 1; cpu = _PR_CreateCPU(me, PR_TRUE); +#ifdef HAVE_CUSTOM_USER_THREADS + me->flags &= (~_PR_GLOBAL_SCOPE); +#endif + _PR_MD_SET_CURRENT_CPU(cpu); _PR_MD_SET_CURRENT_THREAD(cpu->thread); me->cpu = cpu; @@ -212,7 +236,6 @@ static void PR_CALLBACK _PR_CPU_Idle(void *_cpu) while(1) { PRInt32 is; PRIntervalTime timeout; - if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_RUNQ_LOCK(cpu); @@ -243,10 +266,10 @@ static void PR_CALLBACK _PR_CPU_Idle(void *_cpu) _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); @@ -257,19 +280,18 @@ static void PR_CALLBACK _PR_CPU_Idle(void *_cpu) #endif /* _PR_HAVE_ATOMIC_OPS */ #endif - _PR_ClockInterrupt(); + _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); + /* 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) @@ -284,7 +306,11 @@ PR_IMPLEMENT(void) PR_SetConcurrency(PRUintn numCPUs) PRUintn newCPU; PRThread *cpu; + if (!_pr_initialized) _PR_ImplicitInitialization(); + + if (_native_threads_only) + return; _PR_CPU_LIST_LOCK(); if (_pr_numCPU < numCPUs) { diff --git a/pr/src/threads/combined/prucv.c b/pr/src/threads/combined/prucv.c index 90e405d1..78c83b59 100644 --- a/pr/src/threads/combined/prucv.c +++ b/pr/src/threads/combined/prucv.c @@ -21,6 +21,16 @@ #include "prinrval.h" #include "prtypes.h" +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif + /* ** Notify one thread that it has finished waiting on a condition variable @@ -332,6 +342,7 @@ void _PR_ClockInterrupt(void) } thread = _PR_THREAD_PTR(_PR_SLEEPQ(cpu).next); + PR_ASSERT(thread->cpu == cpu); if (elapsed < thread->sleep) { thread->sleep -= elapsed; @@ -345,6 +356,17 @@ void _PR_ClockInterrupt(void) _PR_THREAD_LOCK(thread); + if (thread->cpu != cpu) { + /* + ** The thread was switched to another CPU + ** between the time we unlocked the sleep + ** queue and the time we acquired the thread + ** lock, so it is none of our business now. + */ + _PR_THREAD_UNLOCK(thread); + continue; + } + /* ** Consume this sleeper's amount of elapsed time from the elapsed ** time value. The next remaining piece of elapsed time will be @@ -405,6 +427,13 @@ void _PR_ClockInterrupt(void) int pri = thread->priority; thread->io_suspended = PR_TRUE; +#ifdef WINNT + /* + * For NT, record the cpu on which I/O was issued + * I/O cancellation is done on the same cpu + */ + thread->md.thr_bound_cpu = cpu; +#endif PR_ASSERT(!(thread->flags & _PR_IDLE_THREAD)); PR_ASSERT(thread->cpu == cpu); diff --git a/pr/src/threads/combined/prulock.c b/pr/src/threads/combined/prulock.c index aa20cfed..9b4a11ae 100644 --- a/pr/src/threads/combined/prulock.c +++ b/pr/src/threads/combined/prulock.c @@ -18,6 +18,16 @@ #include "primpl.h" +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif + void _PR_InitLocks(void) { @@ -221,6 +231,13 @@ PR_IMPLEMENT(void) PR_Lock(PRLock *lock) return; #else /* _PR_GLOBAL_THREADS_ONLY */ + if (_native_threads_only) { + PR_ASSERT(lock->owner != me); + _PR_MD_LOCK(&lock->ilock); + lock->owner = me; + return; + } + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); @@ -331,6 +348,12 @@ PR_IMPLEMENT(PRStatus) PR_Unlock(PRLock *lock) return PR_SUCCESS; #else /* _PR_GLOBAL_THREADS_ONLY */ + if (_native_threads_only) { + lock->owner = 0; + _PR_MD_UNLOCK(&lock->ilock); + return PR_SUCCESS; + } + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); _PR_LOCK_LOCK(lock); @@ -399,6 +422,17 @@ PR_IMPLEMENT(PRBool) PR_TestAndLock(PRLock *lock) return PR_FALSE; #else /* _PR_GLOBAL_THREADS_ONLY */ +#ifndef _PR_LOCAL_THREADS_ONLY + if (_native_threads_only) { + is = _PR_MD_TEST_AND_LOCK(&lock->ilock); + if (is == 0) { + lock->owner = me; + return PR_TRUE; + } + return PR_FALSE; + } +#endif + if (!_PR_IS_NATIVE_THREAD(me)) _PR_INTSOFF(is); diff --git a/pr/src/threads/combined/pruthr.c b/pr/src/threads/combined/pruthr.c index 38d84745..372e0b24 100644 --- a/pr/src/threads/combined/pruthr.c +++ b/pr/src/threads/combined/pruthr.c @@ -19,6 +19,15 @@ #include "primpl.h" #include <signal.h> #include <string.h> +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif /* _pr_activeLock protects the following global variables */ PRLock *_pr_activeLock; @@ -121,6 +130,9 @@ void _PR_InitThreads(PRThreadType type, PRThreadPriority priority, thread->flags |= _PR_PRIMORDIAL; #endif + if (_native_threads_only) + thread->flags |= _PR_GLOBAL_SCOPE; + /* * Needs _PR_PRIMORDIAL flag set before calling * _PR_MD_INIT_THREAD() @@ -1095,6 +1107,9 @@ PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, scope = PR_GLOBAL_THREAD; #endif + if (_native_threads_only) + scope = PR_GLOBAL_THREAD; + native = (((scope == PR_GLOBAL_THREAD)|| (scope == PR_GLOBAL_BOUND_THREAD)) && _PR_IS_NATIVE_THREAD_SUPPORTED()); @@ -1319,7 +1334,7 @@ PR_IMPLEMENT(PRThread*) _PR_CreateThread(PRThreadType type, */ PR_Unlock(_pr_activeLock); - if ( (thread->flags & _PR_IDLE_THREAD) || _PR_IS_NATIVE_THREAD(me) ) + if ((! (thread->flags & _PR_IDLE_THREAD)) && _PR_IS_NATIVE_THREAD(me) ) thread->cpu = _PR_GetPrimordialCPU(); else thread->cpu = _PR_MD_CURRENT_CPU(); @@ -1805,8 +1820,13 @@ _PR_AddThreadToRunQ( * "post" the awakened thread to the IO completion port * for the next idle CPU to execute (this is done in * _PR_MD_WAKEUP_WAITER). + * Threads with a suspended I/O operation remain bound to + * the same cpu until I/O is cancelled */ - if (!_PR_IS_NATIVE_THREAD(me) && (cpu == me->cpu)) { + if (!_PR_IS_NATIVE_THREAD(me) && ((cpu == me->cpu) || + (thread->io_suspended))) { + PR_ASSERT(!thread->io_suspended || + (thread->md.thr_bound_cpu == cpu)); _PR_RUNQ_LOCK(cpu); _PR_ADD_RUNQ(thread, cpu, pri); _PR_RUNQ_UNLOCK(cpu); diff --git a/pr/src/threads/prcthr.c b/pr/src/threads/prcthr.c index dae37aa2..9dfb49d1 100644 --- a/pr/src/threads/prcthr.c +++ b/pr/src/threads/prcthr.c @@ -18,6 +18,17 @@ #include "primpl.h" +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif + + extern PRLock *_pr_sleeplock; /* allocated and initialized in prinit */ /* ** Routines common to both native and user threads. @@ -269,17 +280,7 @@ PR_IMPLEMENT(void) SetExecutionEnvironment(PRThread *thread, void *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 */ + return _PR_MD_GETTHREADAFFINITYMASK(thread, mask); #else #if defined(XP_MAC) diff --git a/pr/src/threads/prdump.c b/pr/src/threads/prdump.c index b36899b0..85231130 100644 --- a/pr/src/threads/prdump.c +++ b/pr/src/threads/prdump.c @@ -18,6 +18,16 @@ #include "primpl.h" +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif + /* XXX use unbuffered nspr stdio */ PRFileDesc *_pr_dumpOut; diff --git a/pr/src/threads/prtpd.c b/pr/src/threads/prtpd.c index 5e4b8e31..0fd71e4d 100644 --- a/pr/src/threads/prtpd.c +++ b/pr/src/threads/prtpd.c @@ -51,6 +51,16 @@ #include <string.h> +#if defined(WIN95) +/* +** Some local variables report warnings on Win95 because the code paths +** using them are conditioned on HAVE_CUSTOME_USER_THREADS. +** The pragma suppresses the warning. +** +*/ +#pragma warning(disable : 4101) +#endif + #define _PR_TPD_MODULO 8 /* vectors are extended by this much */ #define _PR_TPD_LIMIT 128 /* arbitary limit on the TPD slots */ static PRInt32 _pr_tpd_length = 0; /* current length of destructor vector */ @@ -158,7 +168,7 @@ PR_IMPLEMENT(PRStatus) PR_SetThreadPrivate(PRUintn his, void *priv) } else { - if ((NULL == self->privateData) || (self->tpdLength <= index)) + if ((NULL == self->privateData) || (self->tpdLength <= (PRUint32)index)) { void *extension = PR_CALLOC(_pr_tpd_length * sizeof(void*)); PR_ASSERT( @@ -212,7 +222,7 @@ PR_IMPLEMENT(void*) PR_GetThreadPrivate(PRUintn his) { PRInt32 index = (PRInt32)his; PRThread *self = PR_GetCurrentThread(); - void *tpd = ((NULL == self->privateData) || (index >= self->tpdLength)) ? + void *tpd = ((NULL == self->privateData) || ((PRUint32)index >= self->tpdLength)) ? NULL : self->privateData[index]; return tpd; @@ -232,7 +242,7 @@ void _PR_DestroyThreadPrivate(PRThread* self) PR_ASSERT(0 != self->tpdLength); do { - for (index = 0; index < self->tpdLength; ++index) + for (index = 0; (PRUint32)index < self->tpdLength; ++index) { void *priv = self->privateData[index]; /* extract */ if (NULL != priv) /* we have data at this index */ diff --git a/pr/tests/Makefile b/pr/tests/Makefile index 33ae4112..49a5bf61 100644 --- a/pr/tests/Makefile +++ b/pr/tests/Makefile @@ -39,7 +39,9 @@ endif CSRCS = \ accept.c \ acceptread.c \ + affinity.c \ alarm.c \ + anonfm.c \ atomic.c \ attach.c \ bigfile.c \ @@ -59,6 +61,7 @@ CSRCS = \ foreign.c \ forktest.c \ fsync.c \ + gethost.c \ getproto.c \ i2l.c \ initclk.c \ @@ -82,6 +85,7 @@ CSRCS = \ multiacc.c \ multiwait.c \ many_cv.c \ + nameshm1.c \ nbconn.c \ nblayer.c \ nonblock.c \ @@ -113,6 +117,11 @@ CSRCS = \ selct_to.c \ select2.c \ sem.c \ + sema.c \ + semaerr.c \ + semaerr1.c \ + semaping.c \ + semapong.c \ server_test.c \ servr_kk.c \ servr_ku.c \ @@ -148,6 +157,7 @@ CSRCS = \ writev.c \ xnotify.c \ y2k.c \ + y2ktmo.c \ $(NULL) ifeq ($(OS_TARGET),OS2) diff --git a/pr/tests/accept.c b/pr/tests/accept.c index e77c2cfe..71f00b7f 100644 --- a/pr/tests/accept.c +++ b/pr/tests/accept.c @@ -118,6 +118,7 @@ ClientThread(void *_action) PRInt32 rv; char buf[CLIENT_DATA]; + memset(buf, 0xaf, sizeof(buf)); /* initialize with arbitrary data */ sock = PR_NewTCPSocket(); if (!sock) { if (!debug_mode) diff --git a/pr/tests/affinity.c b/pr/tests/affinity.c new file mode 100644 index 00000000..fa0f5acb --- /dev/null +++ b/pr/tests/affinity.c @@ -0,0 +1,94 @@ +/* -*- 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.1 (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 "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* + * Test PR_GetThreadAffinityMask + * The function is called by each of local, global and global bound threads + * The test should be run on both single and multi-cpu systems + */ +static void PR_CALLBACK thread_start(void *arg) +{ +PRUint32 mask = 0; + + if (PR_GetThreadAffinityMask(PR_GetCurrentThread(), &mask)) + printf("\tthread_start: PR_GetCurrentThreadAffinityMask failed\n"); + else + printf("\tthread_start: AffinityMask = 0x%x\n",mask); + +} + +int main(int argc, char **argv) +{ + PRThread *t; + + printf("main: creating local thread\n"); + + t = PR_CreateThread(PR_USER_THREAD, + thread_start, 0, + PR_PRIORITY_NORMAL, + PR_LOCAL_THREAD, + PR_JOINABLE_THREAD, + 0); + + if (NULL == t) { + printf("main: cannot create local thread\n"); + exit(1); + } + + PR_JoinThread(t); + + printf("main: creating global thread\n"); + t = PR_CreateThread(PR_USER_THREAD, + thread_start, 0, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + + if (NULL == t) { + printf("main: cannot create global thread\n"); + exit(1); + } + + PR_JoinThread(t); + + printf("main: creating global bound thread\n"); + t = PR_CreateThread(PR_USER_THREAD, + thread_start, 0, + PR_PRIORITY_NORMAL, + PR_GLOBAL_BOUND_THREAD, + PR_JOINABLE_THREAD, + 0); + + if (NULL == t) { + printf("main: cannot create global bound thread\n"); + exit(1); + } + + PR_JoinThread(t); + + return 0; +} diff --git a/pr/tests/anonfm.c b/pr/tests/anonfm.c new file mode 100644 index 00000000..8ce1b15a --- /dev/null +++ b/pr/tests/anonfm.c @@ -0,0 +1,324 @@ +/* -*- 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.1 (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: anonfm.c +** Description: Test anonymous file map +** +** Synopsis: anonfm [options] [dirName] +** +** Options: +** -d enable debug mode +** -h display a help message +** -s <n> size of the anonymous memory map, in KBytes. default: 100KBytes. +** -C 1 Operate this process as ClientOne() +** -C 2 Operate this process as ClientTwo() +** +** anonfn.c contains two tests, corresponding to the two protocols for +** passing an anonymous file map to a child process. +** +** ServerOne()/ClientOne() tests the passing of "raw" file map; it uses +** PR_CreateProcess() [for portability of the test case] to create the +** child process, but does not use the PRProcessAttr structure for +** passing the file map data. +** +** ServerTwo()/ClientTwo() tests the passing of the file map using the +** PRProcessAttr structure. +** +*/ +#include <plgetopt.h> +#include <nspr.h> +#include <private/primpl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* +** Test harness infrastructure +*/ +PRLogModuleInfo *lm; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRUint32 failed_already = 0; + +PRIntn debug = 0; +PRIntn client = 0; /* invoke client, style */ +char dirName[512] = "."; /* directory name to contain anon mapped file */ +PRSize fmSize = (100 * 1024 ); +PRUint32 fmMode = 0600; +PRFileMapProtect fmProt = PR_PROT_READWRITE; +const char *fmEnvName = "nsprFileMapEnvVariable"; + +/* +** Emit help text for this test +*/ +static void Help( void ) +{ + printf("anonfm [options] [dirName]\n"); + printf("-d -- enable debug mode\n"); + printf("dirName is alternate directory name. Default: . (current directory)\n"); + exit(1); +} /* end Help() */ + + +/* +** ClientOne() -- +*/ +static void ClientOne( void ) +{ + PRFileMap *fm; + char *fmString; + char *addr; + PRStatus rc; + + PR_LOG(lm, msgLevel, + ("ClientOne() starting")); + + fmString = PR_GetEnv( fmEnvName ); + if ( NULL == fmString ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_Getenv() failed")); + return; + } + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_Getenv(): found: %s", fmString)); + + fm = PR_ImportFileMapFromString( fmString ); + if ( NULL == fm ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_ImportFileMapFromString() failed")); + return; + } + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_ImportFileMapFromString(): fm: %p", fm )); + + addr = PR_MemMap( fm, 0, fmSize ); + if ( NULL == addr ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_MemMap() failed, OSError: %d", PR_GetOSError() )); + return; + } + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_MemMap(): addr: %p", addr )); + + /* write to memory map to release server */ + *addr = 1; + + rc = PR_MemUnmap( addr, fmSize ); + PR_ASSERT( rc == PR_SUCCESS ); + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_MemUnap(): success" )); + + rc = PR_CloseFileMap( fm ); + if ( PR_FAILURE == rc ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_MemUnap() failed, OSError: %d", PR_GetOSError() )); + return; + } + PR_LOG(lm, msgLevel, + ("ClientOne(): PR_CloseFileMap(): success" )); + + return; +} /* end ClientOne() */ + +/* +** ClientTwo() -- +*/ +static void ClientTwo( void ) +{ + failed_already = 1; +} /* end ClientTwo() */ + +/* +** ServerOne() -- +*/ +static void ServerOne( void ) +{ + PRFileMap *fm; + PRStatus rc; + PRIntn i; + char *addr; + char fmString[256]; + char envBuf[256]; + char *child_argv[8]; + PRProcess *proc; + PRInt32 exit_status; + + PR_LOG(lm, msgLevel, + ("ServerOne() starting")); + + fm = PR_OpenAnonFileMap( dirName, fmSize, fmProt ); + if ( NULL == fm ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("PR_OpenAnonFileMap() failed")); + return; + } + PR_LOG(lm, msgLevel, + ("ServerOne(): FileMap: %p", fm )); + + rc = PR_ExportFileMapAsString( fm, sizeof(fmString), fmString ); + if ( PR_FAILURE == rc ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("PR_ExportFileMap() failed")); + return; + } + + /* + ** put the string into the environment + */ + PR_snprintf( envBuf, sizeof(envBuf), "%s=%s", fmEnvName, fmString); + putenv( envBuf ); + + addr = PR_MemMap( fm, 0, fmSize ); + if ( NULL == addr ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("PR_MemMap() failed")); + return; + } + + /* set initial value for client */ + for (i = 0; i < (PRIntn)fmSize ; i++ ) + *(addr+i) = 0x00; + + PR_LOG(lm, msgLevel, + ("ServerOne(): PR_MemMap(): addr: %p", addr )); + + /* + ** set arguments for child process + */ + child_argv[0] = "anonfm"; + child_argv[1] = "-C"; + child_argv[2] = "1"; + child_argv[3] = NULL; + + proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL); + PR_ASSERT( proc ); + PR_LOG(lm, msgLevel, + ("ServerOne(): PR_CreateProcess(): proc: %x", proc )); + + /* + ** ClientOne() will set the memory to 1 + */ + PR_LOG(lm, msgLevel, + ("ServerOne(): waiting on Client, *addr: %x", *addr )); + while( *addr == 0x00 ) { + if ( debug ) + fprintf(stderr, "."); + PR_Sleep(PR_MillisecondsToInterval(300)); + } + if ( debug ) + fprintf(stderr, "\n"); + PR_LOG(lm, msgLevel, + ("ServerOne(): Client responded" )); + + rc = PR_WaitProcess( proc, &exit_status ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_MemUnmap( addr, fmSize); + if ( PR_FAILURE == rc ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("PR_MemUnmap() failed")); + return; + } + PR_LOG(lm, msgLevel, + ("ServerOne(): PR_MemUnmap(): success" )); + + rc = PR_CloseFileMap(fm); + if ( PR_FAILURE == rc ) { + failed_already = 1; + PR_LOG(lm, msgLevel, + ("PR_CloseFileMap() failed")); + return; + } + PR_LOG(lm, msgLevel, + ("ServerOne(): PR_CloseFileMap() success" )); + + return; +} /* end ServerOne() */ + +/* +** ServerTwo() -- +*/ +static void ServerTwo( void ) +{ + PR_LOG(lm, msgLevel, + ("ServerTwo(): Not implemented yet" )); +} /* end ServerTwo() */ + + +PRIntn main(PRIntn argc, char *argv[]) +{ + { + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "hdC:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) continue; + switch (opt->option) + { + case 'C': /* Client style */ + client = atol(opt->value); + break; + case 's': /* file size */ + fmSize = atol( opt->value ) * 1024; + break; + case 'd': /* debug */ + debug = 1; + msgLevel = PR_LOG_DEBUG; + break; + case 'h': /* help message */ + Help(); + break; + default: + strcpy(dirName, opt->value); + break; + } + } + PL_DestroyOptState(opt); + } + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + + if ( client == 1 ) { + ClientOne(); + } else if ( client == 2 ) { + ClientTwo(); + } else { + ServerOne(); + if ( failed_already ) goto Finished; + ServerTwo(); + } + +Finished: + if ( debug ) + printf("%s\n", (failed_already)? "FAIL" : "PASS"); + return( (failed_already == PR_TRUE )? 1 : 0 ); +} /* main() */ +/* end anonfm.c */ + diff --git a/pr/tests/concur.c b/pr/tests/concur.c index eb3583a1..a87c0dcc 100644 --- a/pr/tests/concur.c +++ b/pr/tests/concur.c @@ -158,6 +158,7 @@ PRIntn PR_CALLBACK Concur(PRIntn argc, char **argv) PR_DestroyLock(context.ml); PR_DestroyCondVar(context.cv); + PR_DELETE(threads); PR_fprintf(PR_STDERR, "PASSED\n"); diff --git a/pr/tests/gethost.c b/pr/tests/gethost.c new file mode 100644 index 00000000..9b13b9c7 --- /dev/null +++ b/pr/tests/gethost.c @@ -0,0 +1,227 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * File: gethost.c + * + * Description: tests various functions in prnetdb.h + * + * Usage: gethost [-6] [hostname] + */ + +#include "prio.h" +#include "prnetdb.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> + +#define DEFAULT_HOST_NAME "www.netscape.com" + +static void Help(void) +{ + fprintf(stderr, "Usage: gethost [-6h] [hostname]\n"); + fprintf(stderr, "\t-6 IPv6 mode (default: FALSE)\n"); + fprintf(stderr, "\t-h help\n"); + fprintf(stderr, "\thostname Name of host (default: %s)\n", + DEFAULT_HOST_NAME); +} /* Help */ + +/* + * Prints the contents of a PRHostEnt structure + */ +void PrintHostent(const PRHostEnt *he) +{ + int i; + int j; + + printf("h_name: %s\n", he->h_name); + for (i = 0; he->h_aliases[i]; i++) { + printf("h_aliases[%d]: %s\n", i, he->h_aliases[i]); + } + printf("h_addrtype: %d\n", he->h_addrtype); + printf("h_length: %d\n", he->h_length); + for (i = 0; he->h_addr_list[i]; i++) { + printf("h_addr_list[%d]: ", i); + for (j = 0; j < he->h_length; j++) { + if (j != 0) printf("."); + printf("%u", (unsigned char)he->h_addr_list[i][j]); + } + printf("\n"); + } +} + +int main(int argc, char **argv) +{ + const char *hostName = DEFAULT_HOST_NAME; + PRHostEnt he, reversehe; + char buf[PR_NETDB_BUF_SIZE]; + char reversebuf[PR_NETDB_BUF_SIZE]; + PRIntn idx; + PRNetAddr addr; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "6h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case '6': /* Enable IPv6 */ + if (PR_SetIPv6Enable(PR_TRUE) == PR_FAILURE) { + fprintf(stderr, "PR_SetIPv6Enable failed\n"); + exit(1); + } + break; + case 0: /* naked */ + hostName = opt->value; + break; + case 'h': /* Help message */ + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (PR_GetHostByName(hostName, buf, sizeof(buf), &he) == PR_FAILURE) { + fprintf(stderr, "PR_GetHostByName failed\n"); + exit(1); + } + PrintHostent(&he); + idx = 0; + while (1) { + idx = PR_EnumerateHostEnt(idx, &he, 0, &addr); + if (idx == -1) { + fprintf(stderr, "PR_EnumerateHostEnt failed\n"); + exit(1); + } + if (idx == 0) break; /* normal loop termination */ + printf("reverse lookup\n"); + if (PR_GetHostByAddr(&addr, reversebuf, sizeof(reversebuf), + &reversehe) == PR_FAILURE) { + fprintf(stderr, "PR_GetHostByAddr failed\n"); + exit(1); + } + PrintHostent(&reversehe); + } + + printf("PR_GetIPNodeByName with PR_AF_INET\n"); + if (PR_GetIPNodeByName(hostName, PR_AF_INET, PR_AI_DEFAULT, + buf, sizeof(buf), &he) == PR_FAILURE) { + fprintf(stderr, "PR_GetIPNodeByName failed\n"); + exit(1); + } + PrintHostent(&he); +#ifdef _PR_INET6 + printf("PR_GetIPNodeByName with PR_AF_INET6\n"); + if (PR_GetIPNodeByName(hostName, PR_AF_INET6, PR_AI_DEFAULT, + buf, sizeof(buf), &he) == PR_FAILURE) { + fprintf(stderr, "PR_GetIPNodeByName failed\n"); + exit(1); + } + PrintHostent(&he); +#endif + + PR_StringToNetAddr("::1", &addr); + PR_StringToNetAddr("127.0.0.1", &addr); + + if (PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) { + fprintf(stderr, "addr should be unspecified address\n"); + exit(1); + } + if (PR_InitializeNetAddr(PR_IpAddrLoopback, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_InitializeNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + + if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) { + fprintf(stderr, "addr should be unspecified address\n"); + exit(1); + } + if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + + addr.inet.family = PR_AF_INET; + addr.inet.port = 0; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) { + fprintf(stderr, "addr should be unspecified address\n"); + exit(1); + } + addr.inet.family = PR_AF_INET; + addr.inet.port = 0; + addr.inet.ip = PR_htonl(PR_INADDR_LOOPBACK); + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + +#if defined(_PR_INET6) + if (PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) { + fprintf(stderr, "addr should be unspecified address\n"); + exit(1); + } + if (PR_SetNetAddr(PR_IpAddrLoopback, PR_AF_INET6, 0, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_SetNetAddr failed\n"); + exit(1); + } + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } + + addr.ipv6.family = PR_AF_INET6; + addr.ipv6.port = 0; + addr.ipv6.ip = in6addr_any; + if (PR_IsNetAddrType(&addr, PR_IpAddrAny) == PR_FALSE) { + fprintf(stderr, "addr should be unspecified address\n"); + exit(1); + } + addr.ipv6.family = PR_AF_INET6; + addr.ipv6.port = 0; + addr.ipv6.ip = in6addr_loopback; + if (PR_IsNetAddrType(&addr, PR_IpAddrLoopback) == PR_FALSE) { + fprintf(stderr, "addr should be loopback address\n"); + exit(1); + } +#endif /* _PR_INET6 */ + + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/io_timeout.c b/pr/tests/io_timeout.c index 1dd85727..aeea212d 100644 --- a/pr/tests/io_timeout.c +++ b/pr/tests/io_timeout.c @@ -160,6 +160,8 @@ dead: if (debug_mode) printf("thread %d is dead\n", info->id); + + PR_Free(info); } void @@ -213,6 +215,9 @@ thread_test(PRThreadScope scope, PRInt32 num_threads) PR_WaitCondVar(dead_cv, PR_INTERVAL_NO_TIMEOUT); } PR_Unlock(dead_lock); + + PR_DestroyCondVar(dead_cv); + PR_DestroyLock(dead_lock); } int main(int argc, char **argv) diff --git a/pr/tests/ioconthr.c b/pr/tests/ioconthr.c index 91b90126..7ed03590 100644 --- a/pr/tests/ioconthr.c +++ b/pr/tests/ioconthr.c @@ -42,6 +42,11 @@ void ThreadFunc(void *arg) err, PR_GetOSError()); PR_ProcessExit(1); } + /* + * After getting an I/O interrupt, this thread must + * close the fd before it exits due to a limitation + * of our NT implementation. + */ if (PR_Close(fd) == PR_FAILURE) { fprintf(stderr, "PR_Close failed\n"); PR_ProcessExit(1); @@ -59,28 +64,20 @@ int main(int argc, char **argv) PRIntervalTime start, elapsed; int index; - fds = (PRFileDesc **) PR_MALLOC(num_threads * sizeof(PRFileDesc *)); + fds = (PRFileDesc **) PR_MALLOC(2 * num_threads * sizeof(PRFileDesc *)); PR_ASSERT(fds != NULL); threads = (PRThread **) PR_MALLOC(num_threads * sizeof(PRThread *)); PR_ASSERT(threads != NULL); - for (index = 0; index < (num_threads / 2); index++) { + for (index = 0; index < num_threads; index++) { if (PR_NewTCPSocketPair(&fds[2 * index]) == PR_FAILURE) { fprintf(stderr, "PR_NewTCPSocket failed\n"); PR_ProcessExit(1); } - - threads[2 * index] = PR_CreateThread( + threads[index] = PR_CreateThread( PR_USER_THREAD, ThreadFunc, fds[2 * index], PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0); - if (NULL == threads[2 * index]) { - fprintf(stderr, "PR_CreateThread failed\n"); - PR_ProcessExit(1); - } - threads[2 * index + 1] = PR_CreateThread( - PR_USER_THREAD, ThreadFunc, fds[2 * index + 1], - PR_PRIORITY_NORMAL, thread_scope, PR_JOINABLE_THREAD, 0); - if (NULL == threads[2 * index + 1]) { + if (NULL == threads[index]) { fprintf(stderr, "PR_CreateThread failed\n"); PR_ProcessExit(1); } @@ -115,6 +112,13 @@ int main(int argc, char **argv) PR_ProcessExit(1); } + for (index = 0; index < num_threads; index++) { + /* fds[2 * index] was passed to and closed by threads[index]. */ + if (PR_Close(fds[2 * index + 1]) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + PR_ProcessExit(1); + } + } PR_DELETE(threads); PR_DELETE(fds); printf("PASS\n"); diff --git a/pr/tests/many_cv.c b/pr/tests/many_cv.c index 1ee2ed25..4b3a2583 100644 --- a/pr/tests/many_cv.c +++ b/pr/tests/many_cv.c @@ -110,6 +110,8 @@ static PRIntn PR_CALLBACK RealMain( PRIntn argc, char **argv ) for (index = 0; index < cvs; ++index) PR_DestroyCondVar(cv[index]); + PR_DELETE(cv); + PR_DestroyLock(ml); printf("PASS\n"); diff --git a/pr/tests/nameshm1.c b/pr/tests/nameshm1.c new file mode 100644 index 00000000..31cbb2aa --- /dev/null +++ b/pr/tests/nameshm1.c @@ -0,0 +1,580 @@ +/* -*- 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.1 (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: nameshm1.c -- Test Named Shared Memory +** +** Description: +** nameshm1 tests Named Shared Memory. nameshm1 performs two tests of +** named shared memory. +** +** The first test is a basic test. The basic test operates as a single +** process. The process exercises all the API elements of the facility. +** This test also attempts to write to all locations in the shared +** memory. +** +** The second test is a client-server test. The client-server test +** creates a new instance of nameshm1, passing the -C argument to the +** new process; this creates the client-side process. The server-side +** (the instance of nameshm1 created from the command line) and the +** client-side interact via inter-process semaphores to verify that the +** shared memory segment can be read and written by both sides in a +** synchronized maner. +** +** Note: Because this test runs in two processes, the log files created +** by the test are not in chronological sequence; makes it hard to read. +** As a temporary circumvention, I changed the definition(s) of the +** _PUT_LOG() macro in prlog.c to force a flushall(), or equivalent. +** This causes the log entries to be emitted in true chronological +** order. +** +** Synopsis: nameshm1 [options] [name] +** +** Options: +** -d Enables debug trace via PR_LOG() +** -v Enables verbose mode debug trace via PR_LOG() +** -w Causes the basic test to attempt to write to the segment +** mapped as read-only. When this option is specified, the +** test should crash with a seg-fault; this is a destructive +** test and is considered successful when it seg-faults. +** +** -C Causes nameshm1 to start as the client-side of a +** client-server pair of processes. Only the instance +** of nameshm1 operating as the server-side process should +** specify the -C option when creating the client-side process; +** the -C option should not be specified at the command line. +** The client-side uses the shared memory segment created by +** the server-side to communicate with the server-side +** process. +** +** -p <n> Specify the number of iterations the client-server tests +** should perform. Default: 1000. +** +** -s <n> Size, in KBytes (1024), of the shared memory segment. +** Default: (10 * 1024) +** +** -i <n> Number of client-side iterations. Default: 3 +** +** name specifies the name of the shared memory segment to be used. +** Default: /tmp/xxxNSPRshm +** +** +** See also: prshm.h +** +** /lth. Aug-1999. +*/ + +#include <plgetopt.h> +#include <nspr.h> +#include <stdlib.h> +#include <string.h> +#include <private/primpl.h> + +#define SEM_NAME1 "/tmp/nameshmSEM1" +#define SEM_NAME2 "/tmp/nameshmSEM2" +#define SEM_MODE 0666 +#define SHM_MODE 0666 + +#define NameSize (1024) + +PRIntn debug = 0; +PRIntn failed_already = 0; +PRLogModuleLevel msgLevel = PR_LOG_NONE; +PRLogModuleInfo *lm; + +/* command line options */ +PRIntn optDebug = 0; +PRIntn optVerbose = 0; +PRUint32 optWriteRO = 0; /* test write to read-only memory. should crash */ +PRUint32 optClient = 0; +PRUint32 optCreate = 1; +PRUint32 optAttachRW = 1; +PRUint32 optAttachRO = 1; +PRUint32 optClose = 1; +PRUint32 optDelete = 1; +PRInt32 optPing = 1000; +PRUint32 optSize = (10 * 1024 ); +PRInt32 optClientIterations = 3; +char optName[NameSize] = "/tmp/xxxNSPRshm"; + +char buf[1024] = ""; + + +static void BasicTest( void ) +{ + PRSharedMemory *shm; + char *addr; /* address of shared memory segment */ + PRUint32 i; + PRInt32 rc; + + PR_LOG( lm, msgLevel, + ( "nameshm1: Begin BasicTest" )); + + if ( PR_FAILURE == PR_DeleteSharedMemory( optName )) { + PR_LOG( lm, msgLevel, + ("nameshm1: Initial PR_DeleteSharedMemory() failed. No problem")); + } else + PR_LOG( lm, msgLevel, + ("nameshm1: Initial PR_DeleteSharedMemory() success")); + + + shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE ); + if ( NULL == shm ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Create: success: %p", shm )); + + addr = PR_AttachSharedMemory( shm , 0 ); + if ( NULL == addr ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Attach: success: %p", addr )); + + /* fill memory with i */ + for ( i = 0; i < optSize ; i++ ) + { + *(addr + i) = i; + } + + rc = PR_DetachSharedMemory( shm, addr ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Detach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Detach: success: " )); + + rc = PR_CloseSharedMemory( shm ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Close: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Close: success: " )); + + rc = PR_DeleteSharedMemory( optName ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Delete: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RW Delete: success: " )); + + PR_LOG( lm, msgLevel, + ("nameshm1: BasicTest(): Passed")); + + return; +} /* end BasicTest() */ + +static void ReadOnlyTest( void ) +{ + PRSharedMemory *shm; + char *roAddr; /* read-only address of shared memory segment */ + PRInt32 rc; + + PR_LOG( lm, msgLevel, + ( "nameshm1: Begin ReadOnlyTest" )); + + shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE); + if ( NULL == shm ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Create: success: %p", shm )); + + + roAddr = PR_AttachSharedMemory( shm , PR_SHM_READONLY ); + if ( NULL == roAddr ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Attach: success: %p", roAddr )); + + if ( optWriteRO ) + { + *roAddr = 0x00; /* write to read-only memory */ + failed_already = 1; + PR_LOG( lm, msgLevel, ("nameshm1: Wrote to read-only memory segment!")); + return; + } + + rc = PR_DetachSharedMemory( shm, roAddr ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Detach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Detach: success: " )); + + rc = PR_CloseSharedMemory( shm ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Close: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Close: success: " )); + + rc = PR_DeleteSharedMemory( optName ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Destroy: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: RO Destroy: success: " )); + + PR_LOG( lm, msgLevel, + ("nameshm1: ReadOnlyTest(): Passed")); + + return; +} /* end ReadOnlyTest() */ + +static void DoClient( void ) +{ + PRStatus rc; + PRSem *sem1, *sem2; + PRSharedMemory *shm; + PRUint32 *addr; + PRInt32 i; + + PR_LOG( lm, msgLevel, + ("nameshm1: DoClient(): Starting")); + + sem1 = PR_OpenSemaphore( SEM_NAME1, 0, 0, 0 ); + PR_ASSERT( sem1 ); + + sem2 = PR_OpenSemaphore( SEM_NAME2, 0, 0, 0 ); + PR_ASSERT( sem1 ); + + shm = PR_OpenSharedMemory( optName, optSize, 0, SHM_MODE ); + if ( NULL == shm ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Create: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Create: success: %p", shm )); + + addr = PR_AttachSharedMemory( shm , 0 ); + if ( NULL == addr ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Attach: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Attach: success: %p", addr )); + + PR_LOG( lm, msgLevel, + ( "Client found: %s", addr)); + + PR_Sleep(PR_SecondsToInterval(4)); + for ( i = 0 ; i < optPing ; i++ ) + { + rc = PR_WaitSemaphore( sem2 ); + PR_ASSERT( PR_FAILURE != rc ); + + (*addr)++; + PR_ASSERT( (*addr % 2) == 0 ); + if ( optVerbose ) + PR_LOG( lm, msgLevel, + ( "nameshm1: Client ping: %d, i: %d", *addr, i)); + + rc = PR_PostSemaphore( sem1 ); + PR_ASSERT( PR_FAILURE != rc ); + } + + rc = PR_CloseSemaphore( sem1 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_CloseSemaphore( sem2 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_DetachSharedMemory( shm, addr ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Detach: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Detach: success: " )); + + rc = PR_CloseSharedMemory( shm ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Close: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: DoClient(): Close: success: " )); + + return; +} /* end DoClient() */ + +static void ClientServerTest( void ) +{ + PRStatus rc; + PRSem *sem1, *sem2; + PRProcess *proc; + PRInt32 exit_status; + PRSharedMemory *shm; + PRUint32 *addr; + PRInt32 i; + char *child_argv[8]; + char buf[24]; + + PR_LOG( lm, msgLevel, + ( "nameshm1: Begin ClientServerTest" )); + + rc = PR_DeleteSharedMemory( optName ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Destroy: failed. No problem")); + } else + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Destroy: success" )); + + + shm = PR_OpenSharedMemory( optName, optSize, (PR_SHM_CREATE | PR_SHM_EXCL), SHM_MODE); + if ( NULL == shm ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Create: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Create: success: %p", shm )); + + addr = PR_AttachSharedMemory( shm , 0 ); + if ( NULL == addr ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Attach: Error: %ld. OSError: %ld", PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Attach: success: %p", addr )); + + sem1 = PR_OpenSemaphore( SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0 ); + PR_ASSERT( sem1 ); + + sem2 = PR_OpenSemaphore( SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 1 ); + PR_ASSERT( sem1 ); + + strcpy( (char*)addr, "FooBar" ); + + child_argv[0] = "nameshm1"; + child_argv[1] = "-C"; + child_argv[2] = "-p"; + sprintf( buf, "%d", optPing ); + child_argv[3] = buf; + child_argv[4] = optName; + child_argv[5] = NULL; + + proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL); + PR_ASSERT( proc ); + + PR_Sleep( PR_SecondsToInterval(4)); + + *addr = 1; + for ( i = 0 ; i < optPing ; i++ ) + { + rc = PR_WaitSemaphore( sem1 ); + PR_ASSERT( PR_FAILURE != rc ); + + (*addr)++; + PR_ASSERT( (*addr % 2) == 1 ); + if ( optVerbose ) + PR_LOG( lm, msgLevel, + ( "nameshm1: Server pong: %d, i: %d", *addr, i)); + + + rc = PR_PostSemaphore( sem2 ); + PR_ASSERT( PR_FAILURE != rc ); + } + + rc = PR_WaitProcess( proc, &exit_status ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_CloseSemaphore( sem1 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_CloseSemaphore( sem2 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_DeleteSemaphore( SEM_NAME1 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_DeleteSemaphore( SEM_NAME2 ); + PR_ASSERT( PR_FAILURE != rc ); + + rc = PR_DetachSharedMemory( shm, addr ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Detach: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Detach: success: " )); + + rc = PR_CloseSharedMemory( shm ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Close: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Close: success: " )); + + rc = PR_DeleteSharedMemory( optName ); + if ( PR_FAILURE == rc ) + { + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Destroy: Error: %ld. OSError: %ld", + PR_GetError(), PR_GetOSError())); + failed_already = 1; + return; + } + PR_LOG( lm, msgLevel, + ( "nameshm1: Server: Destroy: success" )); + + return; +} /* end ClientServerTest() */ + +PRIntn main(PRIntn argc, char *argv[]) +{ + { + /* + ** Get command line options + */ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "Cdvw:s:p:i:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) continue; + switch (opt->option) + { + case 'v': /* debug mode */ + optVerbose = 1; + /* no break! fall into debug option */ + case 'd': /* debug mode */ + debug = 1; + msgLevel = PR_LOG_DEBUG; + break; + case 'w': /* try writing to memory mapped read-only */ + optWriteRO = 1; + break; + case 'C': + optClient = 1; + break; + case 's': + optSize = atol(opt->value) * 1024; + break; + case 'p': + optPing = atol(opt->value); + break; + case 'i': + optClientIterations = atol(opt->value); + break; + default: + strcpy( optName, opt->value ); + break; + } + } + PL_DestroyOptState(opt); + } + + lm = PR_NewLogModule("Test"); /* Initialize logging */ + + PR_LOG( lm, msgLevel, + ( "nameshm1: Starting" )); + + if ( optClient ) + { + DoClient(); + } else { + BasicTest(); + if ( failed_already != 0 ) + goto Finished; + ReadOnlyTest(); + if ( failed_already != 0 ) + goto Finished; + ClientServerTest(); + } + +Finished: + if ( debug ) printf("%s\n", (failed_already)? "FAIL" : "PASS" ); + return( (failed_already)? 1 : 0 ); +} /* main() */ +/* end instrumt.c */ diff --git a/pr/tests/parent.c b/pr/tests/parent.c index 34837e58..e096f9e2 100644 --- a/pr/tests/parent.c +++ b/pr/tests/parent.c @@ -96,6 +96,8 @@ PRIntn main (PRIntn argc, char **argv) child->name, child->argv, NULL, child->attr); t_elapsed = (PRIntervalTime) (PR_IntervalNow() - t_start); + PR_DestroyProcessAttr(child->attr); + test_status = (NULL == child->process) ? 1 : 0; if (NULL != debug) { @@ -126,6 +128,7 @@ PRIntn main (PRIntn argc, char **argv) PR_fprintf(debug, "PR_WaitProcess failed\n"); } } + PR_DELETE(child); PR_Cleanup(); return test_status; diff --git a/pr/tests/poll_to.c b/pr/tests/poll_to.c index 2a4a8979..76942362 100644 --- a/pr/tests/poll_to.c +++ b/pr/tests/poll_to.c @@ -59,7 +59,7 @@ PRIntn debug_mode; int main(int argc, char **argv) { - PRFileDesc *listenSock1, *listenSock2; + PRFileDesc *listenSock1 = NULL, *listenSock2 = NULL; PRUint16 listenPort1, listenPort2; PRNetAddr addr; char buf[128]; @@ -177,9 +177,17 @@ int main(int argc, char **argv) } if (debug_mode) printf("PR_Poll timed out. Test passed.\n\n"); - PR_Cleanup(); - goto exit_now; exit_now: + + if (listenSock1) { + PR_Close(listenSock1); + } + if (listenSock2) { + PR_Close(listenSock2); + } + + PR_Cleanup(); + if(failed_already) return 1; else diff --git a/pr/tests/pollable.c b/pr/tests/pollable.c index 000794e0..da3dffe9 100644 --- a/pr/tests/pollable.c +++ b/pr/tests/pollable.c @@ -267,6 +267,7 @@ int main(int argc, char **argv) PR_DestroyPollableEvent(data[i].event); } PR_DELETE(block); + PR_DestroyPollableEvent(selfData.event); PR_fprintf(PR_STDOUT, "PASSED\n"); return 0; diff --git a/pr/tests/provider.c b/pr/tests/provider.c index bf8c485d..9dfd505e 100644 --- a/pr/tests/provider.c +++ b/pr/tests/provider.c @@ -1372,6 +1372,7 @@ PRIntn main(PRIntn argc, char** argv) PT_FPrintStats(debug_out, thread_type); TimeOfDayMessage("Test exiting at", PR_CurrentThread()); + PR_Cleanup(); return 0; } /* main */ diff --git a/pr/tests/runtests.ksh b/pr/tests/runtests.ksh index 49a0285b..64148079 100755 --- a/pr/tests/runtests.ksh +++ b/pr/tests/runtests.ksh @@ -68,7 +68,9 @@ LOGFILE=${NSPR_TEST_LOGFILE:-"/dev/null"} TESTS=" acceptread accept +affinity alarm +anonfm atomic attach bigfile @@ -84,6 +86,7 @@ exit fileio foreign fsync +gethost getproto i2l initclk @@ -105,6 +108,7 @@ lockfile logger many_cv multiwait +nameshm1 nblayer nonblock op_2long @@ -127,6 +131,9 @@ sel_spd selct_er selct_nm selct_to +sema +semaerr +semaping server_test servr_kk servr_uk @@ -163,11 +170,13 @@ echo "Test\t\t\tResult\n" for prog in $TESTS do echo "$prog\c" +echo "\nBEGIN TEST: $prog\n" >> ${LOGFILE} 2>&1 ./$prog >> ${LOGFILE} 2>&1 if [ 0 = $? ] ; then echo "\t\t\tPassed"; else echo "\t\t\tFAILED"; fi; +echo "\nEND TEST: $prog\n" >> ${LOGFILE} 2>&1 done echo "END\t\t\t`date`" diff --git a/pr/tests/runy2ktests.ksh b/pr/tests/runy2ktests.ksh new file mode 100644 index 00000000..c30322b0 --- /dev/null +++ b/pr/tests/runy2ktests.ksh @@ -0,0 +1,241 @@ +#!/bin/ksh + +# +# The contents of this file are subject to the Netscape Public License +# Version 1.1 (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) 1999 Netscape Communications Corporation. All Rights +# Reserved. +# + +# +# runy2ktests.ksh +# Set system clock to Y2K dates of interest and run the Y2K tests. +# Needs root/administrator privilege +# +# WARNING: Because this script needs to be run with root/administrator +# privilege, thorough understanding of the script and extreme +# caution are urged. +# + +# +# SECTION I +# Define variables +# + +SYSTEM_INFO=`uname -a` +OS_ARCH=`uname -s` +if [ $OS_ARCH = "Windows_NT" ] +then + NULL_DEVICE=nul +else + NULL_DEVICE=/dev/null +fi + +# +# Test dates for NSPR Y2K tests +# +Y2KDATES=" 123123591998.55 + 090923591999.55 + 123123591999.55 + 022823592000.55 + 022923592000.55 + 123123592000.55" + +Y2KDATES_AIX=" 12312359.5598 + 09092359.5599 + 12312359.5599 + 02282359.5500 + 02292359.5500 + 12312359.5500" + +Y2KDATES_HPUX=" 123123591998 + 090923591999 + 123123591999 + 022823592000 + 022923592000 + 123123592000" + +Y2KDATES_MKS=" 1231235998.55 + 0909235999.55 + 1231235999.55 + 0228235900.55 + 0229235900.55 + 1231235900.55" + +# +# NSPR Y2K tests +# +Y2KTESTS=" +y2k \n +y2ktmo \n +y2k \n +../runtests.ksh" + +Y2KTESTS_HPUX=" +y2k \n +y2ktmo -l 60\n +y2k \n +../runtests.ksh" + +# +# SECTION II +# Define functions +# + +save_date() +{ + case $OS_ARCH in + AIX) + SAVED_DATE=`date "+%m%d%H%M.%S%y"` + ;; + HP-UX) + SAVED_DATE=`date "+%m%d%H%M%Y"` + ;; + Windows_NT) + SAVED_DATE=`date "+%m%d%H%M%y.%S"` + ;; + *) + SAVED_DATE=`date "+%m%d%H%M%Y.%S"` + ;; + esac +} + +set_date() +{ + case $OS_ARCH in + Windows_NT) +# +# The date command in MKS Toolkit releases 5.1 and 5.2 +# uses the current DST status for the date we want to +# set the system clock to. However, the DST status for +# that date may be different from the current DST status. +# We can work around this problem by invoking the date +# command with the same date twice. +# + date "$1" > $NULL_DEVICE + date "$1" > $NULL_DEVICE + ;; + *) + date "$1" > $NULL_DEVICE + ;; + esac +} + +restore_date() +{ + set_date "$SAVED_DATE" +} + +savedate() +{ + case $OS_ARCH in + AIX) + SAVED_DATE=`date "+%m%d%H%M.%S%y"` + ;; + HP-UX) + SAVED_DATE=`date "+%m%d%H%M%Y"` + ;; + Windows_NT) + SAVED_DATE=`date "+%m%d%H%M%y.%S"` + ;; + *) + SAVED_DATE=`date "+%m%d%H%M%Y.%S"` + ;; + esac +} + +set_y2k_test_parameters() +{ +# +# set dates +# + case $OS_ARCH in + AIX) + DATES=$Y2KDATES_AIX + ;; + HP-UX) + DATES=$Y2KDATES_HPUX + ;; + Windows_NT) + DATES=$Y2KDATES_MKS + ;; + *) + DATES=$Y2KDATES + ;; + esac + +# +# set tests +# + case $OS_ARCH in + HP-UX) + TESTS=$Y2KTESTS_HPUX + ;; + *) + TESTS=$Y2KTESTS + ;; + esac +} + +# +# runtests: +# - runs each test in $TESTS after setting the +# system clock to each date in $DATES +# + +runtests() +{ +for newdate in ${DATES} +do + set_date $newdate + echo $newdate + echo "BEGIN\t\t\t`date`" + echo "Date\t\t\t\t\tTest\t\t\tResult" + echo $TESTS | while read prog + do + echo "`date`\t\t\c" + echo "$prog\c" + ./$prog >> ${LOGFILE} 2>&1 + if [ 0 = $? ] ; then + echo "\t\t\tPassed"; + else + echo "\t\t\tFAILED"; + fi; + done + echo "END\t\t\t`date`\n" +done + +} + +# +# SECTION III +# Run tests +# + +LOGFILE=${NSPR_TEST_LOGFILE:-$NULL_DEVICE} +OBJDIR=`basename $PWD` +echo "\nNSPR Year 2000 Test Results - $OBJDIR\n" +echo "SYSTEM:\t\t\t${SYSTEM_INFO}" +echo "NSPR_TEST_LOGFILE:\t${LOGFILE}\n" + + +save_date + +# +# Run NSPR Y2k and standard tests +# + +set_y2k_test_parameters +runtests + +restore_date diff --git a/pr/tests/sema.c b/pr/tests/sema.c new file mode 100644 index 00000000..4bcc52bd --- /dev/null +++ b/pr/tests/sema.c @@ -0,0 +1,161 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#define SEM_NAME1 "/tmp/foo.sem" +#define SEM_NAME2 "/tmp/bar.sem" +#define SEM_MODE 0666 +#define ITERATIONS 1000 + +static PRBool debug_mode = PR_FALSE; +static PRIntn iterations = ITERATIONS; +static PRIntn counter; +static PRSem *sem1, *sem2; + +/* + * Thread 2 waits on semaphore 2 and posts to semaphore 1. + */ +void ThreadFunc(void *arg) +{ + PRIntn i; + + for (i = 0; i < iterations; i++) { + if (PR_WaitSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + if (counter == 2*i+1) { + if (debug_mode) printf("thread 2: counter = %d\n", counter); + } else { + fprintf(stderr, "thread 2: counter should be %d but is %d\n", + 2*i+1, counter); + exit(1); + } + counter++; + if (PR_PostSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + } +} + +static void Help(void) +{ + fprintf(stderr, "sema test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PRThread *thred; + PRIntn i; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop count */ + iterations = atoi(opt->value); + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) { + fprintf(stderr, "warning: removed semaphore %s left over " + "from previous run\n", SEM_NAME1); + } + if (PR_DeleteSemaphore(SEM_NAME2) == PR_SUCCESS) { + fprintf(stderr, "warning: removed semaphore %s left over " + "from previous run\n", SEM_NAME2); + } + + sem1 = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 1); + if (NULL == sem1) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + sem2 = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 0); + if (NULL == sem2) { + fprintf(stderr, "PR_OpenSemaphore failed\n"); + exit(1); + } + thred = PR_CreateThread(PR_USER_THREAD, ThreadFunc, NULL, + PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (NULL == thred) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + + /* + * Thread 1 waits on semaphore 1 and posts to semaphore 2. + */ + for (i = 0; i < iterations; i++) { + if (PR_WaitSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + if (counter == 2*i) { + if (debug_mode) printf("thread 1: counter = %d\n", counter); + } else { + fprintf(stderr, "thread 1: counter should be %d but is %d\n", + 2*i, counter); + exit(1); + } + counter++; + if (PR_PostSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + } + + if (PR_JoinThread(thred) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + + if (PR_CloseSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + if (PR_CloseSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + } + if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + } + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/semaerr.c b/pr/tests/semaerr.c new file mode 100644 index 00000000..82953be0 --- /dev/null +++ b/pr/tests/semaerr.c @@ -0,0 +1,123 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#define NO_SUCH_SEM_NAME "/tmp/nosuchsem.sem" +#define SEM_NAME1 "/tmp/foo.sem" +#define SEM_MODE 0666 + +static PRBool debug_mode = PR_FALSE; + +static void Help(void) +{ + fprintf(stderr, "semaerr test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dh"); + PRSem *sem; + char *child_argv[32]; + char **child_arg; + PRProcess *proc; + PRInt32 exit_code; + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + /* + * Open a nonexistent semaphore without the PR_SEM_CREATE + * flag should fail with PR_FILE_NOT_FOUND_ERROR. + */ + (void) PR_DeleteSemaphore(NO_SUCH_SEM_NAME); + sem = PR_OpenSemaphore(NO_SUCH_SEM_NAME, 0, 0, 0); + if (NULL != sem) { + fprintf(stderr, "Opening nonexistent semaphore %s " + "without the PR_SEM_CREATE flag should fail " + "but succeeded\n", NO_SUCH_SEM_NAME); + exit(1); + } + if (PR_GetError() != PR_FILE_NOT_FOUND_ERROR) { + fprintf(stderr, "Expected error is %d but got (%d, %d)\n", + PR_FILE_NOT_FOUND_ERROR, PR_GetError(), PR_GetOSError()); + exit(1); + } + + /* + * Create a semaphore and let the another process + * try PR_SEM_CREATE and PR_SEM_CREATE|PR_SEM_EXCL. + */ + if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) { + fprintf(stderr, "warning: deleted semaphore %s from previous " + "run of the test\n", SEM_NAME1); + } + sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0); + if (sem == NULL) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + child_arg = child_argv; + *child_arg++ = "semaerr1"; + if (debug_mode) { + *child_arg++ = "-d"; + } + *child_arg = NULL; + proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL); + if (proc == NULL) { + fprintf(stderr, "PR_CreateProcess failed\n"); + exit(1); + } + if (PR_WaitProcess(proc, &exit_code) == PR_FAILURE) { + fprintf(stderr, "PR_WaitProcess failed\n"); + exit(1); + } + if (exit_code != 0) { + fprintf(stderr, "process semaerr1 failed\n"); + exit(1); + } + if (PR_CloseSemaphore(sem) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + exit(1); + } + if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/semaerr1.c b/pr/tests/semaerr1.c new file mode 100644 index 00000000..1692cb3b --- /dev/null +++ b/pr/tests/semaerr1.c @@ -0,0 +1,118 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#define SEM_NAME1 "/tmp/foo.sem" +#define SEM_NAME2 "/tmp/bar.sem" +#define SEM_MODE 0666 + +static PRBool debug_mode = PR_FALSE; + +static void Help(void) +{ + fprintf(stderr, "semaerr1 test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dh"); + PRSem *sem; + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + /* + * PR_SEM_CREATE|PR_SEM_EXCL should be able to + * create a nonexistent semaphore. + */ + (void) PR_DeleteSemaphore(SEM_NAME2); + sem = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0); + if (sem == NULL) { + fprintf(stderr, "PR_OpenSemaphore failed\n"); + exit(1); + } + if (PR_CloseSemaphore(sem) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + exit(1); + } + if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + exit(1); + } + + /* + * Opening an existing semaphore with PR_SEM_CREATE|PR_SEM_EXCL. + * should fail with PR_FILE_EXISTS_ERROR. + */ + sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0); + if (sem != NULL) { + fprintf(stderr, "PR_OpenSemaphore should fail but succeeded\n"); + exit(1); + } + if (PR_GetError() != PR_FILE_EXISTS_ERROR) { + fprintf(stderr, "Expect %d but got %d\n", PR_FILE_EXISTS_ERROR, + PR_GetError()); + exit(1); + } + + /* + * Try again, with just PR_SEM_CREATE. This should succeed. + */ + sem = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 0); + if (sem == NULL) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + if (PR_CloseSemaphore(sem) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + exit(1); + } + + sem = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE|PR_SEM_EXCL, SEM_MODE, 0); + if (sem == NULL) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + if (PR_CloseSemaphore(sem) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + exit(1); + } + + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/semaping.c b/pr/tests/semaping.c new file mode 100644 index 00000000..c5430aab --- /dev/null +++ b/pr/tests/semaping.c @@ -0,0 +1,182 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#define SHM_NAME "/tmp/counter" +#define SEM_NAME1 "/tmp/foo.sem" +#define SEM_NAME2 "/tmp/bar.sem" +#define SEM_MODE 0666 +#define SHM_MODE 0666 +#define ITERATIONS 1000 + +static PRBool debug_mode = PR_FALSE; +static PRIntn iterations = ITERATIONS; +static PRSem *sem1, *sem2; + +static void Help(void) +{ + fprintf(stderr, "semaping test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PRProcess *proc; + PRIntn i; + char *child_argv[32]; + char **child_arg; + char iterations_buf[32]; + PRSharedMemory *shm; + PRIntn *counter_addr; + PRInt32 exit_code; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop count */ + iterations = atoi(opt->value); + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (PR_DeleteSharedMemory(SHM_NAME) == PR_SUCCESS) { + fprintf(stderr, "warning: removed shared memory %s left over " + "from previous run\n", SHM_NAME); + } + if (PR_DeleteSemaphore(SEM_NAME1) == PR_SUCCESS) { + fprintf(stderr, "warning: removed semaphore %s left over " + "from previous run\n", SEM_NAME1); + } + if (PR_DeleteSemaphore(SEM_NAME2) == PR_SUCCESS) { + fprintf(stderr, "warning: removed semaphore %s left over " + "from previous run\n", SEM_NAME2); + } + + shm = PR_OpenSharedMemory(SHM_NAME, sizeof(*counter_addr), PR_SHM_CREATE, SHM_MODE); + if (NULL == shm) { + fprintf(stderr, "PR_OpenSharedMemory failed\n"); + exit(1); + } + counter_addr = PR_AttachSharedMemory(shm, 0); + if (NULL == counter_addr) { + fprintf(stderr, "PR_AttachSharedMemory failed\n"); + exit(1); + } + *counter_addr = 0; + sem1 = PR_OpenSemaphore(SEM_NAME1, PR_SEM_CREATE, SEM_MODE, 1); + if (NULL == sem1) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + sem2 = PR_OpenSemaphore(SEM_NAME2, PR_SEM_CREATE, SEM_MODE, 0); + if (NULL == sem2) { + fprintf(stderr, "PR_OpenSemaphore failed\n"); + exit(1); + } + + child_arg = &child_argv[0]; + *child_arg++ = "semapong"; + if (debug_mode != PR_FALSE) { + *child_arg++ = "-d"; + } + if (iterations != ITERATIONS) { + *child_arg++ = "-c"; + PR_snprintf(iterations_buf, sizeof(iterations_buf), "%d", iterations); + *child_arg++ = iterations_buf; + } + *child_arg = NULL; + proc = PR_CreateProcess(child_argv[0], child_argv, NULL, NULL); + if (NULL == proc) { + fprintf(stderr, "PR_CreateProcess failed\n"); + exit(1); + } + + /* + * Process 1 waits on semaphore 1 and posts to semaphore 2. + */ + for (i = 0; i < iterations; i++) { + if (PR_WaitSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + if (*counter_addr == 2*i) { + if (debug_mode) printf("process 1: counter = %d\n", *counter_addr); + } else { + fprintf(stderr, "process 1: counter should be %d but is %d\n", + 2*i, *counter_addr); + exit(1); + } + (*counter_addr)++; + if (PR_PostSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + } + if (PR_DetachSharedMemory(shm, counter_addr) == PR_FAILURE) { + fprintf(stderr, "PR_DetachSharedMemory failed\n"); + exit(1); + } + if (PR_CloseSharedMemory(shm) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSharedMemory failed\n"); + exit(1); + } + if (PR_CloseSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + if (PR_CloseSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + + if (PR_WaitProcess(proc, &exit_code) == PR_FAILURE) { + fprintf(stderr, "PR_WaitProcess failed\n"); + exit(1); + } + if (exit_code != 0) { + fprintf(stderr, "process 2 failed with exit code %d\n", exit_code); + exit(1); + } + + if (PR_DeleteSharedMemory(SHM_NAME) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSharedMemory failed\n"); + } + if (PR_DeleteSemaphore(SEM_NAME1) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + } + if (PR_DeleteSemaphore(SEM_NAME2) == PR_FAILURE) { + fprintf(stderr, "PR_DeleteSemaphore failed\n"); + } + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/semapong.c b/pr/tests/semapong.c new file mode 100644 index 00000000..932a7985 --- /dev/null +++ b/pr/tests/semapong.c @@ -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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> + +#define SHM_NAME "/tmp/counter" +#define SEM_NAME1 "/tmp/foo.sem" +#define SEM_NAME2 "/tmp/bar.sem" +#define ITERATIONS 1000 + +static PRBool debug_mode = PR_FALSE; +static PRIntn iterations = ITERATIONS; +static PRSem *sem1, *sem2; + +static void Help(void) +{ + fprintf(stderr, "semapong test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-c <count> loop count (%d)\n", ITERATIONS); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PRIntn i; + PRSharedMemory *shm; + PRIntn *counter_addr; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dc:h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'c': /* loop count */ + iterations = atoi(opt->value); + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + shm = PR_OpenSharedMemory(SHM_NAME, sizeof(*counter_addr), 0, 0666); + if (NULL == shm) { + fprintf(stderr, "PR_OpenSharedMemory failed\n"); + exit(1); + } + sem1 = PR_OpenSemaphore(SEM_NAME1, 0, 0, 0); + if (NULL == sem1) { + fprintf(stderr, "PR_OpenSemaphore failed (%d, %d)\n", + PR_GetError(), PR_GetOSError()); + exit(1); + } + sem2 = PR_OpenSemaphore(SEM_NAME2, 0, 0, 0); + if (NULL == sem2) { + fprintf(stderr, "PR_OpenSemaphore failed\n"); + exit(1); + } + + counter_addr = PR_AttachSharedMemory(shm, 0); + if (NULL == counter_addr) { + fprintf(stderr, "PR_AttachSharedMemory failed\n"); + exit(1); + } + + /* + * Process 2 waits on semaphore 2 and posts to semaphore 1. + */ + for (i = 0; i < iterations; i++) { + if (PR_WaitSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + if (*counter_addr == 2*i+1) { + if (debug_mode) printf("process 2: counter = %d\n", *counter_addr); + } else { + fprintf(stderr, "process 2: counter should be %d but is %d\n", + 2*i+1, *counter_addr); + exit(1); + } + (*counter_addr)++; + if (PR_PostSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_WaitSemaphore failed\n"); + exit(1); + } + } + if (PR_DetachSharedMemory(shm, counter_addr) == PR_FAILURE) { + fprintf(stderr, "PR_DetachSharedMemory failed\n"); + exit(1); + } + if (PR_CloseSharedMemory(shm) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSharedMemory failed\n"); + exit(1); + } + if (PR_CloseSemaphore(sem1) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + if (PR_CloseSemaphore(sem2) == PR_FAILURE) { + fprintf(stderr, "PR_CloseSemaphore failed\n"); + } + printf("PASS\n"); + return 0; +} diff --git a/pr/tests/socket.c b/pr/tests/socket.c index afac410c..b5accda1 100644 --- a/pr/tests/socket.c +++ b/pr/tests/socket.c @@ -38,11 +38,12 @@ #include <pthread.h> #endif -#ifdef WINNT +#ifdef WIN32 #include <process.h> #endif static int _debug_on = 0; +static int test_cancelio = 0; #ifdef XP_MAC #include "prlog.h" @@ -73,9 +74,25 @@ char *TEST_DIR = "/tmp/prsocket_test_dir"; char *SMALL_FILE_NAME = "/tmp/prsocket_test_dir/small_file"; char *LARGE_FILE_NAME = "/tmp/prsocket_test_dir/large_file"; #endif -#define SMALL_FILE_SIZE (8 * 1024) /* 8 KB */ -#define SMALL_FILE_HEADER_SIZE (64) /* 64 bytes */ -#define LARGE_FILE_SIZE (3 * 1024 * 1024) /* 3 MB */ +#define SMALL_FILE_SIZE (3 * 1024) /* 3 KB */ +#define SMALL_FILE_OFFSET_1 (512) +#define SMALL_FILE_LEN_1 (1 * 1024) /* 1 KB */ +#define SMALL_FILE_OFFSET_2 (75) +#define SMALL_FILE_LEN_2 (758) +#define SMALL_FILE_OFFSET_3 (1024) +#define SMALL_FILE_LEN_3 (SMALL_FILE_SIZE - SMALL_FILE_OFFSET_3) +#define SMALL_FILE_HEADER_SIZE (64) /* 64 bytes */ +#define SMALL_FILE_TRAILER_SIZE (128) /* 128 bytes */ + +#define LARGE_FILE_SIZE (3 * 1024 * 1024) /* 3 MB */ +#define LARGE_FILE_OFFSET_1 (0) +#define LARGE_FILE_LEN_1 (2 * 1024 * 1024) /* 2 MB */ +#define LARGE_FILE_OFFSET_2 (64) +#define LARGE_FILE_LEN_2 (1 * 1024 * 1024 + 75) +#define LARGE_FILE_OFFSET_3 (2 * 1024 * 1024 - 128) +#define LARGE_FILE_LEN_3 (LARGE_FILE_SIZE - LARGE_FILE_OFFSET_3) +#define LARGE_FILE_HEADER_SIZE (512) +#define LARGE_FILE_TRAILER_SIZE (64) #define BUF_DATA_SIZE (2 * 1024) #define TCP_MESG_SIZE 1024 @@ -84,7 +101,7 @@ char *LARGE_FILE_NAME = "/tmp/prsocket_test_dir/large_file"; * local host will not be lost */ #define UDP_DGRAM_SIZE 128 -#define NUM_TCP_CLIENTS 10 +#define NUM_TCP_CLIENTS 5 /* for a listen queue depth of 5 */ #define NUM_UDP_CLIENTS 10 #ifndef XP_MAC @@ -150,16 +167,33 @@ readn(PRFileDesc *sockfd, char *buf, int len) int rem; int bytes; int offset = 0; + int err, oserr; + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT; + + if (test_cancelio) + timeout = PR_SecondsToInterval(2); for (rem=len; rem; offset += bytes, rem -= bytes) { DPRINTF(("thread = 0x%lx: calling PR_Recv, bytes = %d\n", PR_GetCurrentThread(), rem)); +retry: bytes = PR_Recv(sockfd, buf + offset, rem, 0, - PR_INTERVAL_NO_TIMEOUT); + timeout); DPRINTF(("thread = 0x%lx: returning from PR_Recv, bytes = %d\n", PR_GetCurrentThread(), bytes)); - if (bytes <= 0) - return -1; + if (bytes < 0) { +#ifdef WINNT + printf("PR_Recv: error = %d oserr = %d\n",(err = PR_GetError()), + PR_GetOSError()); + if ((test_cancelio) && (err == PR_IO_TIMEOUT_ERROR)) { + if (PR_NT_CancelIo(sockfd) != PR_SUCCESS) + printf("PR_NT_CancelIO: error = %d\n",PR_GetError()); + timeout = PR_INTERVAL_NO_TIMEOUT; + goto retry; + } +#endif + return -1; + } } return len; } @@ -262,7 +296,7 @@ PRThread* create_new_thread(PRThreadType type, PRInt32 native_thread = 0; PR_ASSERT(state == PR_UNJOINABLE_THREAD); -#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WINNT) || defined(WIN95) +#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WIN32) switch(index % 4) { case 0: scope = (PR_LOCAL_THREAD); @@ -289,6 +323,7 @@ PRInt32 native_thread = 0; return (NULL); #else HANDLE thandle; + unsigned tid; thandle = (HANDLE) _beginthreadex( NULL, @@ -296,7 +331,7 @@ PRInt32 native_thread = 0; (unsigned (__stdcall *)(void *))start, arg, 0, - NULL); + &tid); return((PRThread *) thandle); #endif } else { @@ -474,13 +509,6 @@ UDP_Server(void *arg) failed_already=1; return; } - if (netaddr.inet.port != PR_htons(UDP_SERVER_PORT)) { - fprintf(stderr,"prsocket_test: ERROR - tried to bind to UDP " - "port %hu, but was bound to port %hu\n", - UDP_SERVER_PORT, PR_ntohs(netaddr.inet.port)); - failed_already=1; - return; - } DPRINTF(("PR_Bind: UDP Server netaddr.inet.ip = 0x%lx, netaddr.inet.port = %d\n", netaddr.inet.ip, netaddr.inet.port)); @@ -533,6 +561,7 @@ UDP_Server(void *arg) } PR_DELETE(in_buf); + PR_Close(sockfd); /* * Decrement exit_counter and notify parent thread @@ -580,14 +609,15 @@ TCP_Client(void *arg) netaddr.inet.ip = cp->server_addr.inet.ip; for (i = 0; i < num_tcp_connections_per_client; i++) { - if ((sockfd = PR_NewTCPSocket()) == NULL) { - fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n"); + if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) { + fprintf(stderr,"prsocket_test: PR_OpenTCPSocket failed\n"); failed_already=1; return; } if (PR_Connect(sockfd, &netaddr,PR_INTERVAL_NO_TIMEOUT) < 0){ - fprintf(stderr,"prsocket_test: PR_Connect failed\n"); + fprintf(stderr, "PR_Connect failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); failed_already=1; return; } @@ -599,6 +629,10 @@ TCP_Client(void *arg) /* * write to server */ +#ifdef WINNT + if (test_cancelio && (j == 0)) + PR_Sleep(PR_SecondsToInterval(12)); +#endif if (writen(sockfd, out_buf->data, bytes) < bytes) { fprintf(stderr,"prsocket_test: ERROR - TCP_Client:writen\n"); failed_already=1; @@ -676,8 +710,8 @@ UDP_Client(void *arg) failed_already=1; return; } - if ((sockfd = PR_NewUDPSocket()) == NULL) { - fprintf(stderr,"prsocket_test: PR_NewUDPSocket failed\n"); + if ((sockfd = PR_OpenUDPSocket(PR_AF_INET)) == NULL) { + fprintf(stderr,"prsocket_test: PR_OpenUDPSocket failed\n"); failed_already=1; return; } @@ -1023,6 +1057,7 @@ UDP_Socket_Client_Server_Test(void) static PRFileDesc *small_file_fd, *large_file_fd; static void *small_file_addr, *small_file_header, *large_file_addr; +static void *small_file_trailer, *large_file_header, *large_file_trailer; /* * TransmitFile_Client * Client Thread @@ -1034,14 +1069,17 @@ TransmitFile_Client(void *arg) union PRNetAddr netaddr; char *small_buf, *large_buf; Client_Param *cp = (Client_Param *) arg; + PRInt32 rlen; - small_buf = (char*)PR_Malloc(SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE); + small_buf = (char*)PR_Malloc(SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE + + SMALL_FILE_TRAILER_SIZE); if (small_buf == NULL) { fprintf(stderr,"prsocket_test: failed to alloc buffer\n"); failed_already=1; return; } - large_buf = (char*)PR_Malloc(LARGE_FILE_SIZE); + large_buf = (char*)PR_Malloc(LARGE_FILE_SIZE + LARGE_FILE_HEADER_SIZE + + LARGE_FILE_TRAILER_SIZE); if (large_buf == NULL) { fprintf(stderr,"prsocket_test: failed to alloc buffer\n"); failed_already=1; @@ -1103,10 +1141,225 @@ TransmitFile_Client(void *arg) failed_already=1; } #endif + + + /* + * receive data from PR_SendFile + */ + /* + * case 1: small file with header and trailer + */ + rlen = SMALL_FILE_SIZE + SMALL_FILE_HEADER_SIZE + + SMALL_FILE_TRAILER_SIZE; + if (readn(sockfd, small_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 1. ERROR - small file header corruption\n"); + failed_already=1; + return; + } + if (memcmp(small_file_addr, small_buf + SMALL_FILE_HEADER_SIZE, + SMALL_FILE_SIZE) != 0) { + fprintf(stderr, + "SendFile 1. ERROR - small file data corruption\n"); + failed_already=1; + return; + } + if (memcmp(small_file_trailer, + small_buf + SMALL_FILE_HEADER_SIZE + SMALL_FILE_SIZE, + SMALL_FILE_TRAILER_SIZE) != 0) { + fprintf(stderr, + "SendFile 1. ERROR - small file trailer corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 2: partial large file at zero offset, file with header and trailer + */ + rlen = LARGE_FILE_LEN_1 + LARGE_FILE_HEADER_SIZE + + LARGE_FILE_TRAILER_SIZE; + if (readn(sockfd, large_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 2. ERROR - large file header corruption\n"); + failed_already=1; + return; + } + if (memcmp(large_file_addr, large_buf + LARGE_FILE_HEADER_SIZE, + LARGE_FILE_LEN_1) != 0) { + fprintf(stderr, + "SendFile 2. ERROR - large file data corruption\n"); + failed_already=1; + return; + } + if (memcmp(large_file_trailer, + large_buf + LARGE_FILE_HEADER_SIZE + LARGE_FILE_LEN_1, + LARGE_FILE_TRAILER_SIZE) != 0) { + fprintf(stderr, + "SendFile 2. ERROR - large file trailer corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 3: partial small file at non-zero offset, with header + */ + rlen = SMALL_FILE_LEN_1 + SMALL_FILE_HEADER_SIZE; + if (readn(sockfd, small_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 3. ERROR - small file header corruption\n"); + failed_already=1; + return; + } + if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_1, + small_buf + SMALL_FILE_HEADER_SIZE, SMALL_FILE_LEN_1) != 0) { + fprintf(stderr, + "SendFile 3. ERROR - small file data corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 4: partial small file at non-zero offset, with trailer + */ + rlen = SMALL_FILE_LEN_2 + SMALL_FILE_TRAILER_SIZE; + if (readn(sockfd, small_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_2, small_buf, + SMALL_FILE_LEN_2) != 0) { + fprintf(stderr, + "SendFile 4. ERROR - small file data corruption\n"); + failed_already=1; + return; + } + if (memcmp(small_file_trailer, small_buf + SMALL_FILE_LEN_2, + SMALL_FILE_TRAILER_SIZE) != 0) { + fprintf(stderr, + "SendFile 4. ERROR - small file trailer corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 5: partial large file at non-zero offset, file with header + */ + rlen = LARGE_FILE_LEN_2 + LARGE_FILE_HEADER_SIZE; + if (readn(sockfd, large_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 5. ERROR - large file header corruption\n"); + failed_already=1; + return; + } + if (memcmp((char *)large_file_addr + LARGE_FILE_OFFSET_2, + large_buf + LARGE_FILE_HEADER_SIZE, + LARGE_FILE_LEN_2) != 0) { + fprintf(stderr, + "SendFile 5. ERROR - large file data corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 6: partial small file at non-zero offset, with header + */ + rlen = SMALL_FILE_LEN_3 + SMALL_FILE_HEADER_SIZE; + if (readn(sockfd, small_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(small_file_header, small_buf, SMALL_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 6. ERROR - small file header corruption\n"); + return; + } + if (memcmp((char *) small_file_addr + SMALL_FILE_OFFSET_3, + small_buf + SMALL_FILE_HEADER_SIZE, SMALL_FILE_LEN_3) != 0) { +#if 0 + char *i, *j; + int k; + + i = (char *) small_file_addr + SMALL_FILE_OFFSET_3; + j = small_buf + SMALL_FILE_HEADER_SIZE; + k = SMALL_FILE_LEN_3; + while (k-- > 0) { + if (*i++ != *j++) + printf("i = %d j = %d\n", + (int) (i - ((char *) small_file_addr + SMALL_FILE_OFFSET_3)), + (int) (j - (small_buf + SMALL_FILE_HEADER_SIZE))); + } +#endif + fprintf(stderr, + "SendFile 6. ERROR - small file data corruption\n"); + failed_already=1; + return; + } +#endif + /* + * case 7: partial large file at non-zero offset, with trailer + */ + rlen = LARGE_FILE_LEN_3 + LARGE_FILE_HEADER_SIZE; + if (readn(sockfd, large_buf, rlen) != rlen) { + fprintf(stderr, + "prsocket_test: SendFile_Client failed to receive file\n"); + failed_already=1; + return; + } +#ifdef XP_UNIX + if (memcmp(large_file_header, large_buf, LARGE_FILE_HEADER_SIZE) != 0){ + fprintf(stderr, + "SendFile 7. ERROR - large file header corruption\n"); + failed_already=1; + return; + } + if (memcmp((char *)large_file_addr + LARGE_FILE_OFFSET_3, + large_buf + LARGE_FILE_HEADER_SIZE, + LARGE_FILE_LEN_3) != 0) { + fprintf(stderr, + "SendFile 7. ERROR - large file data corruption\n"); + failed_already=1; + return; + } +#endif PR_DELETE(small_buf); PR_DELETE(large_buf); PR_Close(sockfd); + /* * Decrement exit_counter and notify parent thread */ @@ -1132,6 +1385,8 @@ Serve_TransmitFile_Client(void *arg) PRInt32 bytes; PRFileDesc *local_small_file_fd=NULL; PRFileDesc *local_large_file_fd=NULL; + PRSendFileData sfd; + PRInt32 slen; sockfd = scp->sockfd; local_small_file_fd = PR_Open(SMALL_FILE_NAME, PR_RDONLY,0); @@ -1160,13 +1415,175 @@ Serve_TransmitFile_Client(void *arg) failed_already=1; } bytes = PR_TransmitFile(sockfd, local_large_file_fd, NULL, 0, - PR_TRANSMITFILE_CLOSE_SOCKET, PR_INTERVAL_NO_TIMEOUT); + PR_TRANSMITFILE_KEEP_OPEN, PR_INTERVAL_NO_TIMEOUT); if (bytes != LARGE_FILE_SIZE) { fprintf(stderr, "prsocket_test: PR_TransmitFile failed: (%ld, %ld)\n", PR_GetError(), PR_GetOSError()); failed_already=1; } + + /* + * PR_SendFile test cases + */ + + /* + * case 1: small file with header and trailer + */ + sfd.fd = local_small_file_fd; + sfd.file_offset = 0; + sfd.file_nbytes = 0; + sfd.header = small_file_header; + sfd.hlen = SMALL_FILE_HEADER_SIZE; + sfd.trailer = small_file_trailer; + sfd.tlen = SMALL_FILE_TRAILER_SIZE; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = SMALL_FILE_SIZE+ SMALL_FILE_HEADER_SIZE + + SMALL_FILE_TRAILER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 1. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + + /* + * case 2: partial large file at zero offset, file with header and trailer + */ + sfd.fd = local_large_file_fd; + sfd.file_offset = 0; + sfd.file_nbytes = LARGE_FILE_LEN_1; + sfd.header = large_file_header; + sfd.hlen = LARGE_FILE_HEADER_SIZE; + sfd.trailer = large_file_trailer; + sfd.tlen = LARGE_FILE_TRAILER_SIZE; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = LARGE_FILE_LEN_1 + LARGE_FILE_HEADER_SIZE + + LARGE_FILE_TRAILER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 2. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + /* + * case 3: partial small file at non-zero offset, with header + */ + sfd.fd = local_small_file_fd; + sfd.file_offset = SMALL_FILE_OFFSET_1; + sfd.file_nbytes = SMALL_FILE_LEN_1; + sfd.header = small_file_header; + sfd.hlen = SMALL_FILE_HEADER_SIZE; + sfd.trailer = NULL; + sfd.tlen = 0; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = SMALL_FILE_LEN_1 + SMALL_FILE_HEADER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 3. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + /* + * case 4: partial small file at non-zero offset, with trailer + */ + sfd.fd = local_small_file_fd; + sfd.file_offset = SMALL_FILE_OFFSET_2; + sfd.file_nbytes = SMALL_FILE_LEN_2; + sfd.header = NULL; + sfd.hlen = 0; + sfd.trailer = small_file_trailer; + sfd.tlen = SMALL_FILE_TRAILER_SIZE; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = SMALL_FILE_LEN_2 + SMALL_FILE_TRAILER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 4. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + /* + * case 5: partial large file at non-zero offset, file with header + */ + sfd.fd = local_large_file_fd; + sfd.file_offset = LARGE_FILE_OFFSET_2; + sfd.file_nbytes = LARGE_FILE_LEN_2; + sfd.header = large_file_header; + sfd.hlen = LARGE_FILE_HEADER_SIZE; + sfd.trailer = NULL; + sfd.tlen = 0; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = LARGE_FILE_LEN_2 + LARGE_FILE_HEADER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 5. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + /* + * case 6: partial small file from non-zero offset till end of file, with header + */ + sfd.fd = local_small_file_fd; + sfd.file_offset = SMALL_FILE_OFFSET_3; + sfd.file_nbytes = 0; /* data from offset to end-of-file */ + sfd.header = small_file_header; + sfd.hlen = SMALL_FILE_HEADER_SIZE; + sfd.trailer = NULL; + sfd.tlen = 0; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_KEEP_OPEN, + PR_INTERVAL_NO_TIMEOUT); + slen = SMALL_FILE_LEN_3 + SMALL_FILE_HEADER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 6. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } + /* + * case 7: partial large file at non-zero offset till end-of-file, with header + */ + sfd.fd = local_large_file_fd; + sfd.file_offset = LARGE_FILE_OFFSET_3; + sfd.file_nbytes = 0; /* data until end-of-file */ + sfd.header = large_file_header; + sfd.hlen = LARGE_FILE_HEADER_SIZE; + sfd.trailer = NULL; + sfd.tlen = 0; + bytes = PR_SendFile(sockfd, &sfd, PR_TRANSMITFILE_CLOSE_SOCKET, + PR_INTERVAL_NO_TIMEOUT); + slen = LARGE_FILE_LEN_3 + LARGE_FILE_HEADER_SIZE; + if (bytes != slen) { + fprintf(stderr, + "socket: Error - 7. PR_SendFile send_size = %d, bytes sent = %d\n", + slen, bytes); + fprintf(stderr, + "prsocket_test: PR_SendFile failed: (%ld, %ld)\n", + PR_GetError(), PR_GetOSError()); + failed_already=1; + } done: if (local_small_file_fd != NULL) PR_Close(local_small_file_fd); @@ -1199,8 +1616,8 @@ TransmitFile_Server(void *arg) /* * Create a tcp socket */ - if ((sockfd = PR_NewTCPSocket()) == NULL) { - fprintf(stderr,"prsocket_test: PR_NewTCPSocket failed\n"); + if ((sockfd = PR_OpenTCPSocket(PR_AF_INET)) == NULL) { + fprintf(stderr,"prsocket_test: PR_OpenTCPSocket failed\n"); failed_already=1; goto exit; } @@ -1392,7 +1809,7 @@ Socket_Misc_Test(void) * map the small file; used in checking for data corruption */ small_file_addr = mmap(0, SMALL_FILE_SIZE, PROT_READ, - MAP_PRIVATE, small_file_fd->secret->md.osfd, 0); + MAP_SHARED, small_file_fd->secret->md.osfd, 0); if (small_file_addr == (void *) -1) { fprintf(stderr,"prsocket_test failed to mmap file %s\n", SMALL_FILE_NAME); @@ -1414,6 +1831,18 @@ Socket_Misc_Test(void) memset(small_file_header, (int) PR_IntervalNow(), SMALL_FILE_HEADER_SIZE); /* + * trailer for small file + */ + small_file_trailer = PR_MALLOC(SMALL_FILE_TRAILER_SIZE); + if (small_file_trailer == NULL) { + fprintf(stderr,"prsocket_test failed to malloc header trailer\n"); + failed_already=1; + rv = -1; + goto done; + } + memset(small_file_trailer, (int) PR_IntervalNow(), + SMALL_FILE_TRAILER_SIZE); + /* * setup large file */ large_file_fd = PR_Open(LARGE_FILE_NAME, PR_RDWR | PR_CREATE_FILE,0777); @@ -1452,7 +1881,7 @@ Socket_Misc_Test(void) * map the large file; used in checking for data corruption */ large_file_addr = mmap(0, LARGE_FILE_SIZE, PROT_READ, - MAP_PRIVATE, large_file_fd->secret->md.osfd, 0); + MAP_SHARED, large_file_fd->secret->md.osfd, 0); if (large_file_addr == (void *) -1) { fprintf(stderr,"prsocket_test failed to mmap file %s\n", LARGE_FILE_NAME); @@ -1461,6 +1890,31 @@ Socket_Misc_Test(void) goto done; } #endif + /* + * header for large file + */ + large_file_header = PR_MALLOC(LARGE_FILE_HEADER_SIZE); + if (large_file_header == NULL) { + fprintf(stderr,"prsocket_test failed to malloc header file\n"); + failed_already=1; + rv = -1; + goto done; + } + memset(large_file_header, (int) PR_IntervalNow(), + LARGE_FILE_HEADER_SIZE); + /* + * trailer for large file + */ + large_file_trailer = PR_MALLOC(LARGE_FILE_TRAILER_SIZE); + if (large_file_trailer == NULL) { + fprintf(stderr,"prsocket_test failed to malloc header trailer\n"); + failed_already=1; + rv = -1; + goto done; + } + memset(large_file_trailer, (int) PR_IntervalNow(), + LARGE_FILE_TRAILER_SIZE); + datalen = tcp_mesg_size; thread_count = 0; /* @@ -1629,6 +2083,7 @@ main(int argc, char **argv) goto done; } else printf("TCP_Socket_Client_Server_Test Passed\n"); + test_cancelio = 0; /* * run client-server test with UDP */ diff --git a/pr/tests/testfile.c b/pr/tests/testfile.c index ffc9798a..b6b03882 100644 --- a/pr/tests/testfile.c +++ b/pr/tests/testfile.c @@ -111,7 +111,7 @@ PRThread* create_new_thread(PRThreadType type, PRInt32 native_thread = 0; PR_ASSERT(state == PR_UNJOINABLE_THREAD); -#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WINNT) || defined(WIN95) +#if (defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)) || defined(WIN32) switch(index % 4) { case 0: scope = (PR_LOCAL_THREAD); @@ -138,15 +138,15 @@ PRInt32 native_thread = 0; return (NULL); #else HANDLE thandle; + unsigned tid; - printf("creating Windows thread\n"); thandle = (HANDLE) _beginthreadex( NULL, stackSize, (unsigned (__stdcall *)(void *))start, arg, 0, - NULL); + &tid); return((PRThread *) thandle); #endif } else { @@ -950,11 +950,11 @@ int main(int argc, char **argv) } #ifdef WIN32 len = GetTempPath(TMPDIR_LEN, testdir); - if ((len > 0) && (len < (TMPDIR_LEN - 7))) { + if ((len > 0) && (len < (TMPDIR_LEN - 6))) { /* - * enough space for \prdir + * enough space for prdir */ - strcpy((testdir + len),"\prdir"); + strcpy((testdir + len),"prdir"); TEST_DIR = testdir; printf("TEST_DIR = %s\n",TEST_DIR); } diff --git a/pr/tests/tmoacc.c b/pr/tests/tmoacc.c index aa0fea66..bda47e19 100644 --- a/pr/tests/tmoacc.c +++ b/pr/tests/tmoacc.c @@ -174,6 +174,7 @@ static void Accept(void *arg) PRIntn Tmoacc(PRIntn argc, char **argv) { PRStatus rv; + PRIntn exitStatus; PRIntn index; Shared *shared; PLOptStatus os; @@ -190,7 +191,7 @@ PRIntn Tmoacc(PRIntn argc, char **argv) PR_SetIPv6Enable(PR_TRUE); #endif - shared = PR_NEWZAP(Shared); /* this is leaked */ + shared = PR_NEWZAP(Shared); shared->debug = NULL; shared->passed = PR_TRUE; @@ -232,6 +233,7 @@ PRIntn Tmoacc(PRIntn argc, char **argv) if (0 == timeout) timeout = DEFAULT_TIMEOUT; PR_STDIO_INIT(); + memset(&listenAddr, 0, sizeof(listenAddr)); rv = PR_InitializeNetAddr(PR_IpAddrAny, BASE_PORT, &listenAddr); PR_ASSERT(PR_SUCCESS == rv); @@ -297,7 +299,9 @@ PRIntn Tmoacc(PRIntn argc, char **argv) PR_GetSpecialFD(PR_StandardError), "%s\n", ((shared->passed) ? "PASSED" : "FAILED")); - return (shared->passed) ? 0 : 1; + exitStatus = (shared->passed) ? 0 : 1; + PR_DELETE(shared); + return exitStatus; } int main(int argc, char **argv) diff --git a/pr/tests/tmocon.c b/pr/tests/tmocon.c index ecf5e52a..d3bb9839 100644 --- a/pr/tests/tmocon.c +++ b/pr/tests/tmocon.c @@ -102,11 +102,7 @@ static void CauseTimeout(const Shared *shared) static PRStatus MakeReceiver(Shared *shared) { PRStatus rv = PR_FAILURE; -#if defined(_PR_INET6) - if (IN6_IS_ADDR_LOOPBACK(&shared->serverAddress.ipv6.ip)) -#else - if (PR_htonl(PR_INADDR_LOOPBACK) == shared->serverAddress.inet.ip) -#endif + if (PR_IsNetAddrType(&shared->serverAddress, PR_IpAddrLoopback)) { char *argv[3]; char path[1024 + sizeof("/tmoacc")]; @@ -183,7 +179,12 @@ static void Connect(void *arg) */ PR_Sleep(shared->dally); - if (shared->debug > 1) PR_fprintf(debug_out, "connecting ... "); + if (shared->debug > 1) + { + char buf[128]; + PR_NetAddrToString(&shared->serverAddress, buf, sizeof(buf)); + PR_fprintf(debug_out, "connecting to %s ... ", buf); + } rv = PR_Connect( clientSock, &shared->serverAddress, Timeout(shared)); if (PR_SUCCESS == rv) @@ -252,55 +253,6 @@ static void Connect(void *arg) PR_DELETE(buffer); } /* Connect */ -#ifdef _PR_INET6 - -/* - * Below are some of the IPv6 hosts at Netscape and their IPv4 - * addresses. We will use their IPv4-compatible IPv6 addresses - * for automatic tunneling. - */ - -static const char *ipv6_host[] = { - "dijkstra", - "wirth", - "gandalf", - "raven" -}; - -static const unsigned char ipv6_host_ipv4_addr[] = { - 208, 12, 62, 49, - 208, 12, 62, 98, - 208, 12, 62, 55, - 206, 222, 228, 80 -}; - -/* - * Return PR_TRUE if 'host' is an IPv6 host at Netscape in the table - * above, and fill 'addr->ipv6.ip' with its IPv4-compatible IPv6 address - * for automatic tunneling. - */ - -static PRBool -IPv6AutoTunnelHost(const char *host, PRNetAddr *addr) -{ - int nHosts = sizeof(ipv6_host) / sizeof(ipv6_host[0]); - int i; - - for (i = 0; i < nHosts; i++) - { - if (strcmp(host, ipv6_host[i]) == 0) - { - memset(&addr->ipv6.ip, 0, 12); - memcpy(((unsigned char *) &addr->ipv6.ip) + 12, - &ipv6_host_ipv4_addr[4 * i], 4); - PR_ASSERT(IN6_IS_ADDR_V4COMPAT(&addr->ipv6.ip)); - return PR_TRUE; - } - } - return PR_FALSE; -} -#endif /* _PR_INET6 */ - int Tmocon(int argc, char **argv) { /* @@ -319,6 +271,7 @@ int Tmocon(int argc, char **argv) */ PRStatus rv; + int exitStatus; PLOptStatus os; Shared *shared = NULL; PRThread **thread = NULL; @@ -330,7 +283,7 @@ int Tmocon(int argc, char **argv) #ifdef _PR_INET6 PR_SetIPv6Enable(PR_TRUE); #endif - shared = PR_NEWZAP(Shared); /* this is leaked */ + shared = PR_NEWZAP(Shared); shared->debug = 0; shared->failed = PR_FALSE; @@ -339,6 +292,7 @@ int Tmocon(int argc, char **argv) shared->message_length = DEFAULT_MESSAGESIZE; PR_STDIO_INIT(); + memset(&shared->serverAddress, 0, sizeof(shared->serverAddress)); rv = PR_InitializeNetAddr(PR_IpAddrLoopback, BASE_PORT, &shared->serverAddress); PR_ASSERT(PR_SUCCESS == rv); @@ -364,21 +318,14 @@ int Tmocon(int argc, char **argv) break; case 'h': /* the value for backlock */ { -#ifdef _PR_INET6 - if (!IPv6AutoTunnelHost(opt->value, &shared->serverAddress)) - { -#endif - PRIntn es = 0; - PRHostEnt host; - char buffer[1024]; - (void)PR_GetHostByName( - opt->value, buffer, sizeof(buffer), &host); - es = PR_EnumerateHostEnt( - es, &host, BASE_PORT, &shared->serverAddress); - PR_ASSERT(es > 0); -#ifdef _PR_INET6 - } -#endif + PRIntn es = 0; + PRHostEnt host; + char buffer[1024]; + (void)PR_GetHostByName( + opt->value, buffer, sizeof(buffer), &host); + es = PR_EnumerateHostEnt( + es, &host, BASE_PORT, &shared->serverAddress); + PR_ASSERT(es > 0); } break; case 'm': /* number of messages to send */ @@ -425,7 +372,9 @@ int Tmocon(int argc, char **argv) PR_fprintf( PR_GetSpecialFD(PR_StandardError), "%s\n", ((shared->failed) ? "FAILED" : "PASSED")); - return (shared->failed) ? 1 : 0; + exitStatus = (shared->failed) ? 1 : 0; + PR_DELETE(shared); + return exitStatus; } int main(int argc, char **argv) diff --git a/pr/tests/vercheck.c b/pr/tests/vercheck.c index aef966a1..b616ad2a 100644 --- a/pr/tests/vercheck.c +++ b/pr/tests/vercheck.c @@ -33,12 +33,12 @@ #include <stdlib.h> /* - * This release (3.1) is backward compatible with all + * This release (3.5) is backward compatible with all * the previous releases. It, of course, is compatible * with itself. */ static char *compatible_version[] = { - "2.1 19980529", "3.0", "3.0.1", PR_VERSION + "2.1 19980529", "3.0", "3.0.1", "3.1", "3.1.1", "3.1.2", PR_VERSION }; /* @@ -46,8 +46,7 @@ static char *compatible_version[] = { * patches. */ static char *incompatible_version[] = { - "3.1.2", - "3.5", "3.5.1", + "3.5.1", "4.0", "4.0.3", "10.0", "11.1", "12.14.20" }; diff --git a/pr/tests/y2k.c b/pr/tests/y2k.c index c08351f2..7439e780 100644 --- a/pr/tests/y2k.c +++ b/pr/tests/y2k.c @@ -19,6 +19,8 @@ /* * file: y2k.c * description: Test for y2k compliance for NSPR. + * + * Sep 1999. lth. Added "Sun" specified dates to the test data. */ /*********************************************************************** ** Includes @@ -41,6 +43,8 @@ extern void SetupMacPrintfLog(char *logFile); #endif +#define PRINT_DETAILS + int failed_already=0; PRBool debug_mode = PR_FALSE; @@ -154,7 +158,13 @@ static PRTime prt[] = { LL_INIT(221612, 2107598848), /* 951818400000000 */ LL_INIT(228975, 663398400), /* 983440800000000 */ LL_INIT(258365, 1974568960), /* 1109671200000000 */ - LL_INIT(218132, 1393788928) /* 936871200000000 */ + LL_INIT(218132, 1393788928), /* 936871200000000 */ + /* Sun's dates follow */ + LL_INIT( 213062, 4077979648 ), /* Dec 31 1998 10:00:00 */ + LL_INIT( 218152, 1894443008 ), /* Sep 10 1999 10:00:00 */ + LL_INIT( 221592, 1606944768 ), /* Feb 28 2000 10:00:00 */ + LL_INIT( 227768, 688924672 ), /* Dec 31 2000 10:00:00 */ + LL_INIT( 227788, 1189578752 ), /* Jan 1 2001 10:00:00 */ }; static PRExplodedTime gmt[] = { @@ -163,7 +173,13 @@ static PRExplodedTime gmt[] = { { 0, 0, 0, 10, 29, 1, 2000, 2, 59, {0, 0}}, /* 2000/02/29 10:00:00 GMT */ { 0, 0, 0, 10, 1, 2, 2001, 4, 59, {0, 0}}, /* 2001/3/1 10:00:00 GMT */ { 0, 0, 0, 10, 1, 2, 2005, 2, 59, {0, 0}}, /* 2005/3/1 10:00:00 GMT */ - { 0, 0, 0, 10, 9, 8, 1999, 4, 251, {0, 0}} /* 1999/9/9 10:00:00 GMT */ + { 0, 0, 0, 10, 9, 8, 1999, 4, 251, {0, 0}}, /* 1999/9/9 10:00:00 GMT */ + /* Sun's dates follow */ + { 0, 0, 0, 10, 31, 11, 1998, 4, 364, {0, 0}}, /* 12/31/1998 10:00:00 GMT */ + { 0, 0, 0, 10, 10, 8, 1999, 5, 252, {0, 0}}, /* 9/10/1999 10:00:00 GMT */ + { 0, 0, 0, 10, 28, 1, 2000, 1, 58, {0, 0}}, /* 2/28/2000 10:00:00 GMT */ + { 0, 0, 0, 10, 31, 11, 2000, 0, 365, {0, 0}}, /* 12/31/2000 10:00:00 GMT */ + { 0, 0, 0, 10, 1, 0, 2001, 1, 0, {0, 0}} /* 1/1/2001 10:00:00 GMT */ }; static PRExplodedTime uspt[] = { @@ -172,7 +188,13 @@ static PRExplodedTime uspt[] = { { 0, 0, 0, 2, 29, 1, 2000, 2, 59, {-28800, 0}}, /* 2000/02/29 2:00:00 PST */ { 0, 0, 0, 2, 1, 2, 2001, 4, 59, {-28800, 0}}, /* 2001/3/1 2:00:00 PST */ { 0, 0, 0, 2, 1, 2, 2005, 2, 59, {-28800, 0}}, /* 2005/3/1 2:00:00 PST */ -{ 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}} /* 1999/9/9 3:00:00 PDT */ +{ 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}}, /* 1999/9/9 3:00:00 PDT */ + /* Sun's dates follow */ + { 0, 0, 0, 2, 31, 11, 1998, 4, 364, {-28800, 0}}, /* 12/31/1998 00:00:00 GMT */ + { 0, 0, 0, 3, 10, 8, 1999, 5, 252, {-28800, 3600}}, /* 9/10/1999 00:00:00 GMT */ + { 0, 0, 0, 2, 28, 1, 2000, 1, 58, {-28800, 0}}, /* 2/28/2000 00:00:00 GMT */ + { 0, 0, 0, 2, 31, 11, 2000, 0, 365, {-28800, 0}}, /* 12/31/2000 00:00:00 GMT */ + { 0, 0, 0, 2, 1, 0, 2001, 1, 0, {-28800, 0}} /* 1/1/2001 00:00:00 GMT */ }; /* @@ -188,7 +210,13 @@ static PRExplodedTime localt[] = { { 0, 0, 0, 2, 29, 1, 2000, 2, 59, {-28800, 0}}, /* 2000/02/29 2:00:00 PST */ { 0, 0, 0, 2, 1, 2, 2001, 4, 59, {-28800, 0}}, /* 2001/3/1 2:00:00 PST */ { 0, 0, 0, 2, 1, 2, 2005, 2, 59, {-28800, 0}}, /* 2005/3/1 2:00:00 PST */ -{ 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}} /* 1999/9/9 3:00:00 PDT */ +{ 0, 0, 0, 3, 9, 8, 1999, 4, 251, {-28800, 3600}}, /* 1999/9/9 3:00:00 PDT */ + /* Sun's dates follow */ + { 0, 0, 0, 2, 31, 11, 1998, 4, 364, {-28800, 0}}, /* 12/31/1998 00:00:00 GMT */ + { 0, 0, 0, 3, 10, 8, 1999, 5, 252, {-28800, 3600}}, /* 9/10/1999 00:00:00 GMT */ + { 0, 0, 0, 2, 28, 1, 2000, 1, 58, {-28800, 0}}, /* 2/28/2000 00:00:00 GMT */ + { 0, 0, 0, 2, 31, 11, 2000, 0, 365, {-28800, 0}}, /* 12/31/2000 00:00:00 GMT */ + { 0, 0, 0, 2, 1, 0, 2001, 1, 0, {-28800, 0}} /* 1/1/2001 00:00:00 GMT */ }; #ifdef US_EASTERN_TIME @@ -198,7 +226,13 @@ static PRExplodedTime localt[] = { { 0, 0, 0, 5, 29, 1, 2000, 2, 59, {-18000, 0}}, /* 2000/02/29 2:00:00 EST */ { 0, 0, 0, 5, 1, 2, 2001, 4, 59, {-18000, 0}}, /* 2001/3/1 2:00:00 EST */ { 0, 0, 0, 5, 1, 2, 2005, 2, 59, {-18000, 0}}, /* 2005/3/1 2:00:00 EST */ -{ 0, 0, 0, 6, 9, 8, 1999, 4, 251, {-18000, 3600}} /* 1999/9/9 3:00:00 EDT */ +{ 0, 0, 0, 6, 9, 8, 1999, 4, 251, {-18000, 3600}}, /* 1999/9/9 3:00:00 EDT */ + /* Sun's dates follow */ + { 0, 0, 0, 5, 31, 11, 1998, 4, 364, {-18000 0}}, /* 12/31/1998 00:00:00 GMT */ + { 0, 0, 0, 6, 10, 8, 1999, 5, 252, {-18000 3600}}, /* 9/10/1999 00:00:00 GMT */ + { 0, 0, 0, 5, 28, 1, 2000, 1, 58, {-18000 0}}, /* 2/28/2000 00:00:00 GMT */ + { 0, 0, 0, 5, 31, 11, 2000, 0, 365, {-18000 0}}, /* 12/31/2000 00:00:00 GMT */ + { 0, 0, 0, 5, 1, 0, 2001, 1, 0, {-18000 0}} /* 1/1/2001 00:00:00 GMT */ }; #endif @@ -217,6 +251,8 @@ static PRStatus TestExplodeImplodeTime(void) PR_ExplodeTime(prt[idx], PR_GMTParameters, &et_tmp); if (!ExplodedTimeIsEqual(&et_tmp, &gmt[idx])) { fprintf(stderr, "GMT not equal\n"); + PrintExplodedTime(&et_tmp); + PrintExplodedTime(&gmt[idx]); exit(1); } prt_tmp = PR_ImplodeTime(&et_tmp); @@ -233,6 +269,8 @@ static PRStatus TestExplodeImplodeTime(void) PR_ExplodeTime(prt[idx], PR_USPacificTimeParameters, &et_tmp); if (!ExplodedTimeIsEqual(&et_tmp, &uspt[idx])) { fprintf(stderr, "US Pacific Time not equal\n"); + PrintExplodedTime(&et_tmp); + PrintExplodedTime(&uspt[idx]); exit(1); } prt_tmp = PR_ImplodeTime(&et_tmp); @@ -249,6 +287,8 @@ static PRStatus TestExplodeImplodeTime(void) PR_ExplodeTime(prt[idx], PR_LocalTimeParameters, &et_tmp); if (!ExplodedTimeIsEqual(&et_tmp, &localt[idx])) { fprintf(stderr, "not equal\n"); + PrintExplodedTime(&et_tmp); + PrintExplodedTime(&localt[idx]); exit(1); } prt_tmp = PR_ImplodeTime(&et_tmp); @@ -512,6 +552,35 @@ static ParseTest parseArray[] = { "69-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}}, { "69/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2069, 2, 364, {-28800, 0 }}}, + /* "Sun". 31-Dec-1998 (?) */ + { "Thu 31 Dec 1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "12/31/98 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "12/31/1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "12-31-98 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "12-31-1998 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "98-12-31 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + { "98/12/31 00:00:00", { 00000, 00, 00, 00, 31, 11, 1998, 4, 364, {-28800, 0 }}}, + + /* 09-Sep-1999. Interesting because of its use as an eof marker? */ + { "09 Sep 1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "9/9/99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "9/9/1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "9-9-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "9-9-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "09-09-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "09-09-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + { "99-09-09 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, + + /* "Sun". 10-Sep-1999. Because Sun said so. */ + { "10 Sep 1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "9/10/99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "9/10/1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "9-10-99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "9-10-1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "09-10-99 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "09-10-1999 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + { "99-09-10 00:00:00", { 000000, 00, 00, 00, 10, 8, 1999, 5, 252, {-28800, 3600 }}}, + /* 31-Dec-1999 */ { "31 Dec 1999 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}}, { "12/31/99 00:00:00", { 000000, 00, 00, 00, 31, 11, 1999, 5, 364, {-28800, 0 }}}, @@ -530,6 +599,15 @@ static ParseTest parseArray[] = { "01-01-00 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, { "Saturday 01-01-2000 00:00:00", { 000000, 00, 00, 00, 1, 0, 2000, 6, 0, {-28800, 0 }}}, + /* "Sun". 28-Feb-2000 */ + { "28 Feb 2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "2/28/00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "2/28/2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "2-28-00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "2-28-2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "02-28-00 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + { "02-28-2000 00:00:00", { 000000, 00, 00, 00, 28, 1, 2000, 1, 58, {-28800, 0 }}}, + /* 29-Feb-2000 */ { "29 Feb 2000 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}}, { "2/29/00 00:00:00", { 000000, 00, 00, 00, 29, 1, 2000, 2, 59, {-28800, 0 }}}, @@ -547,6 +625,24 @@ static ParseTest parseArray[] = { "03-01-00 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, { "03-01-2000 00:00:00", { 000000, 00, 00, 00, 1, 2, 2000, 3, 60, {-28800, 0 }}}, + /* "Sun". 31-Dec-2000 */ + { "31 Dec 2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "12/31/00 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "12/31/2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "12-31-00 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "12-31-2000 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "00-12-31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + { "00/12/31 00:00:00", { 000000, 00, 00, 00, 31, 11, 2000, 0, 365, {-28800, 0 }}}, + + /* "Sun". 01-Jan-2001 */ + { "01 Jan 2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "1/1/01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "1/1/2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "1-1-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "1-1-2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "01-01-01 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + { "Saturday 01-01-2001 00:00:00", { 000000, 00, 00, 00, 1, 0, 2001, 1, 0, {-28800, 0 }}}, + /* 01-Mar-2001 */ { "01 Mar 2001 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}}, { "3/1/01 00:00:00", { 000000, 00, 00, 00, 1, 2, 2001, 4, 59, {-28800, 0 }}}, @@ -581,16 +677,6 @@ static ParseTest parseArray[] = { "03-01-05 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, { "03-01-2005 00:00:00", { 000000, 00, 00, 00, 1, 2, 2005, 2, 59, {-28800, 0 }}}, - /* 09-Sep-1999. Interesting because of its use as an eof marker? */ - { "09 Sep 1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "9/9/99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "9/9/1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "9-9-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "9-9-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "09-09-99 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "09-09-1999 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - { "99-09-09 00:00:00", { 000000, 00, 00, 00, 9, 8, 1999, 4, 251, {-28800, 3600 }}}, - /* last element. string must be null */ { NULL } }; /* end array of ParseTest */ diff --git a/pr/tests/y2ktmo.c b/pr/tests/y2ktmo.c new file mode 100644 index 00000000..0eab0b3d --- /dev/null +++ b/pr/tests/y2ktmo.c @@ -0,0 +1,527 @@ +/* -*- 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.1 (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) 1999 Netscape Communications Corporation. All Rights + * Reserved. + */ + +/* + * Test: y2ktmo + * + * Description: + * This test tests the interval time facilities in NSPR for Y2K + * compliance. All the functions that take a timeout argument + * are tested: PR_Sleep, socket I/O (PR_Accept is taken as a + * representative), PR_Poll, PR_WaitCondVar, PR_Wait, and + * PR_CWait. A thread of each thread scope (local, global, and + * global bound) is created to call each of these functions. + * The test should be started at the specified number of seconds + * (called the lead time) before a Y2K rollover test date. The + * timeout values for these threads will span over the rollover + * date by at least the specified number of seconds. For + * example, if the lead time is 5 seconds, the test should + * be started at time (D - 5), where D is a rollover date, and + * the threads will time out at or after time (D + 5). The + * timeout values for the threads are spaced one second apart. + * + * When a thread times out, it calls PR_IntervalNow() to verify + * that it did wait for the specified time. In addition, it + * calls a platform-native function to verify the actual elapsed + * time again, to rule out the possibility that PR_IntervalNow() + * is broken. We allow the actual elapsed time to deviate from + * the specified timeout by a certain tolerance (in milliseconds). + */ + +#include "nspr.h" +#include "plgetopt.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#if defined(XP_UNIX) +#include <sys/time.h> /* for gettimeofday */ +#endif +#if defined(WIN32) +#include <sys/types.h> +#include <sys/timeb.h> /* for _ftime */ +#endif + +#define DEFAULT_LEAD_TIME_SECS 5 +#define DEFAULT_TOLERANCE_MSECS 500 + +static PRBool debug_mode = PR_FALSE; +static PRInt32 lead_time_secs = DEFAULT_LEAD_TIME_SECS; +static PRInt32 tolerance_msecs = DEFAULT_TOLERANCE_MSECS; +static PRIntervalTime start_time; +static PRIntervalTime tolerance; + +#if defined(XP_UNIX) +static struct timeval start_time_tv; +#endif +#if defined(WIN32) +static struct _timeb start_time_tb; +#endif + +static void SleepThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + + if (PR_Sleep(timeout) == PR_FAILURE) { + fprintf(stderr, "PR_Sleep failed\n"); + exit(1); + } + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + if (debug_mode) { + fprintf(stderr, "Sleep thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +static void AcceptThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + PRFileDesc *sock; + PRNetAddr addr; + PRFileDesc *accepted; + + sock = PR_NewTCPSocket(); + if (sock == NULL) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + addr.inet.family = PR_AF_INET; + addr.inet.port = 0; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + if (PR_Bind(sock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + if (PR_Listen(sock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + accepted = PR_Accept(sock, NULL, timeout); + if (accepted != NULL || PR_GetError() != PR_IO_TIMEOUT_ERROR) { + fprintf(stderr, "PR_Accept did not time out\n"); + exit(1); + } + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (debug_mode) { + fprintf(stderr, "Accept thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +static void PollThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + PRFileDesc *sock; + PRNetAddr addr; + PRPollDesc pd; + PRIntn rv; + + sock = PR_NewTCPSocket(); + if (sock == NULL) { + fprintf(stderr, "PR_NewTCPSocket failed\n"); + exit(1); + } + memset(&addr, 0, sizeof(addr)); + addr.inet.family = PR_AF_INET; + addr.inet.port = 0; + addr.inet.ip = PR_htonl(PR_INADDR_ANY); + if (PR_Bind(sock, &addr) == PR_FAILURE) { + fprintf(stderr, "PR_Bind failed\n"); + exit(1); + } + if (PR_Listen(sock, 5) == PR_FAILURE) { + fprintf(stderr, "PR_Listen failed\n"); + exit(1); + } + pd.fd = sock; + pd.in_flags = PR_POLL_READ; + rv = PR_Poll(&pd, 1, timeout); + if (rv != 0) { + fprintf(stderr, "PR_Poll did not time out\n"); + exit(1); + } + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + if (PR_Close(sock) == PR_FAILURE) { + fprintf(stderr, "PR_Close failed\n"); + exit(1); + } + if (debug_mode) { + fprintf(stderr, "Poll thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +static void WaitCondVarThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + PRLock *ml; + PRCondVar *cv; + + ml = PR_NewLock(); + if (ml == NULL) { + fprintf(stderr, "PR_NewLock failed\n"); + exit(1); + } + cv = PR_NewCondVar(ml); + if (cv == NULL) { + fprintf(stderr, "PR_NewCondVar failed\n"); + exit(1); + } + PR_Lock(ml); + PR_WaitCondVar(cv, timeout); + PR_Unlock(ml); + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + PR_DestroyCondVar(cv); + PR_DestroyLock(ml); + if (debug_mode) { + fprintf(stderr, "wait cond var thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +static void WaitMonitorThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + PRMonitor *mon; + + mon = PR_NewMonitor(); + if (mon == NULL) { + fprintf(stderr, "PR_NewMonitor failed\n"); + exit(1); + } + PR_EnterMonitor(mon); + PR_Wait(mon, timeout); + PR_ExitMonitor(mon); + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + PR_DestroyMonitor(mon); + if (debug_mode) { + fprintf(stderr, "wait monitor thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +static void WaitCMonitorThread(void *arg) +{ + PRIntervalTime timeout = (PRIntervalTime) arg; + PRIntervalTime elapsed; +#if defined(XP_UNIX) || defined(WIN32) + PRInt32 timeout_msecs = PR_IntervalToMilliseconds(timeout); + PRInt32 elapsed_msecs; +#endif +#if defined(XP_UNIX) + struct timeval end_time_tv; +#endif +#if defined(WIN32) + struct _timeb end_time_tb; +#endif + int dummy; + + PR_CEnterMonitor(&dummy); + PR_CWait(&dummy, timeout); + PR_CExitMonitor(&dummy); + elapsed = (PRIntervalTime)(PR_IntervalNow() - start_time); + if (elapsed + tolerance < timeout || elapsed > timeout + tolerance) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#if defined(XP_UNIX) + gettimeofday(&end_time_tv, NULL); + elapsed_msecs = 1000*(end_time_tv.tv_sec - start_time_tv.tv_sec) + + (end_time_tv.tv_usec - start_time_tv.tv_usec)/1000; +#endif +#if defined(WIN32) + _ftime(&end_time_tb); + elapsed_msecs = 1000*(end_time_tb.time - start_time_tb.time) + + (end_time_tb.millitm - start_time_tb.millitm); +#endif +#if defined(XP_UNIX) || defined(WIN32) + if (elapsed_msecs + tolerance_msecs < timeout_msecs + || elapsed_msecs > timeout_msecs + tolerance_msecs) { + fprintf(stderr, "timeout wrong\n"); + exit(1); + } +#endif + if (debug_mode) { + fprintf(stderr, "wait cached monitor thread (scope %d) done\n", + PR_GetThreadScope(PR_GetCurrentThread())); + } +} + +typedef void (*NSPRThreadFunc)(void*); + +static NSPRThreadFunc threadFuncs[] = { + SleepThread, AcceptThread, PollThread, + WaitCondVarThread, WaitMonitorThread, WaitCMonitorThread}; + +static PRThreadScope threadScopes[] = { + PR_LOCAL_THREAD, PR_GLOBAL_THREAD, PR_GLOBAL_BOUND_THREAD}; + +static void Help(void) +{ + fprintf(stderr, "y2ktmo test program usage:\n"); + fprintf(stderr, "\t-d debug mode (FALSE)\n"); + fprintf(stderr, "\t-l <secs> lead time (%d)\n", + DEFAULT_LEAD_TIME_SECS); + fprintf(stderr, "\t-t <msecs> tolerance (%d)\n", + DEFAULT_TOLERANCE_MSECS); + fprintf(stderr, "\t-h this message\n"); +} /* Help */ + +int main(int argc, char **argv) +{ + PRThread **threads; + int num_thread_funcs = sizeof(threadFuncs)/sizeof(NSPRThreadFunc); + int num_thread_scopes = sizeof(threadScopes)/sizeof(PRThreadScope); + int i, j; + int idx; + PRInt32 secs; + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "dl:t:h"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) { + if (PL_OPT_BAD == os) continue; + switch (opt->option) { + case 'd': /* debug mode */ + debug_mode = PR_TRUE; + break; + case 'l': /* lead time */ + lead_time_secs = atoi(opt->value); + break; + case 't': /* tolerance */ + tolerance_msecs = atoi(opt->value); + break; + case 'h': + default: + Help(); + return 2; + } + } + PL_DestroyOptState(opt); + + if (debug_mode) { + fprintf(stderr, "lead time: %d secs\n", lead_time_secs); + fprintf(stderr, "tolerance: %d msecs\n", tolerance_msecs); + } + + start_time = PR_IntervalNow(); +#if defined(XP_UNIX) + gettimeofday(&start_time_tv, NULL); +#endif +#if defined(WIN32) + _ftime(&start_time_tb); +#endif + tolerance = PR_MillisecondsToInterval(tolerance_msecs); + + threads = PR_Malloc( + num_thread_scopes * num_thread_funcs * sizeof(PRThread*)); + if (threads == NULL) { + fprintf(stderr, "PR_Malloc failed\n"); + exit(1); + } + + /* start to time out 5 seconds after a rollover date */ + secs = lead_time_secs + 5; + idx = 0; + for (i = 0; i < num_thread_scopes; i++) { + for (j = 0; j < num_thread_funcs; j++) { + threads[idx] = PR_CreateThread(PR_USER_THREAD, threadFuncs[j], + (void*)PR_SecondsToInterval(secs), PR_PRIORITY_NORMAL, + threadScopes[i], PR_JOINABLE_THREAD, 0); + if (threads[idx] == NULL) { + fprintf(stderr, "PR_CreateThread failed\n"); + exit(1); + } + secs++; + idx++; + } + } + for (idx = 0; idx < num_thread_scopes*num_thread_funcs; idx++) { + if (PR_JoinThread(threads[idx]) == PR_FAILURE) { + fprintf(stderr, "PR_JoinThread failed\n"); + exit(1); + } + } + PR_Free(threads); + printf("PASS\n"); + return 0; +} |